From 4c0f559d23b4ffc5084884b95ad0b956fa95db9c Mon Sep 17 00:00:00 2001 From: "dgrove@google.com" Date: Wed, 5 Oct 2011 05:20:07 +0000 Subject: [PATCH] Initial checkin. git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@15 260f80e4-7a28-3924-810f-c04153c831b5 --- runtime/PRESUBMIT.py | 46 + runtime/bin/bin.gypi | 411 + runtime/bin/builtin.cc | 29 + runtime/bin/builtin.dart | 13 + runtime/bin/builtin.h | 42 + runtime/bin/builtin_in.cc | 156 + runtime/bin/dartutils.cc | 57 + runtime/bin/dartutils.h | 36 + runtime/bin/directory.cc | 65 + runtime/bin/directory.dart | 57 + runtime/bin/directory.h | 26 + runtime/bin/directory_impl.dart | 130 + runtime/bin/directory_linux.cc | 72 + runtime/bin/directory_macos.cc | 72 + runtime/bin/directory_win.cc | 24 + runtime/bin/eventhandler.cc | 68 + runtime/bin/eventhandler.dart | 28 + runtime/bin/eventhandler.h | 50 + runtime/bin/eventhandler_linux.cc | 327 + runtime/bin/eventhandler_linux.h | 57 + runtime/bin/eventhandler_macos.cc | 325 + runtime/bin/eventhandler_macos.h | 57 + runtime/bin/eventhandler_win.cc | 778 ++ runtime/bin/eventhandler_win.h | 333 + runtime/bin/fdutils.h | 41 + runtime/bin/fdutils_linux.cc | 104 + runtime/bin/fdutils_macos.cc | 104 + runtime/bin/file.cc | 243 + runtime/bin/file.dart | 65 + runtime/bin/file.h | 78 + runtime/bin/file_impl.dart | 223 + runtime/bin/file_linux.cc | 128 + runtime/bin/file_macos.cc | 128 + runtime/bin/file_test.cc | 60 + runtime/bin/file_win.cc | 130 + runtime/bin/gen_snapshot.cc | 186 + runtime/bin/globals.h | 126 + runtime/bin/input_stream.dart | 25 + runtime/bin/main.cc | 257 + runtime/bin/output_stream.dart | 19 + runtime/bin/process.cc | 70 + runtime/bin/process.dart | 66 + runtime/bin/process.h | 31 + runtime/bin/process_impl.dart | 153 + runtime/bin/process_linux.cc | 302 + runtime/bin/process_macos.cc | 302 + runtime/bin/process_script.cc | 158 + runtime/bin/process_script.h | 60 + runtime/bin/process_test.cc | 57 + runtime/bin/process_win.cc | 89 + runtime/bin/run_vm_tests.cc | 98 + runtime/bin/set.h | 128 + runtime/bin/set_test.cc | 125 + runtime/bin/snapshot_empty.cc | 16 + runtime/bin/snapshot_in.cc | 22 + runtime/bin/socket.cc | 143 + runtime/bin/socket.dart | 116 + runtime/bin/socket.h | 37 + runtime/bin/socket_impl.dart | 287 + runtime/bin/socket_linux.cc | 138 + runtime/bin/socket_macos.cc | 138 + runtime/bin/socket_stream.dart | 166 + runtime/bin/socket_win.cc | 147 + runtime/bin/timer.dart | 19 + runtime/bin/timer_impl.dart | 162 + runtime/codereview.settings | 4 + runtime/dart-runtime.gyp | 33 + runtime/include/dart_api.h | 395 + runtime/lib/array.cc | 142 + runtime/lib/array.dart | 294 + runtime/lib/arrays.dart | 90 + runtime/lib/bool.dart | 6 + runtime/lib/clock.cc | 26 + runtime/lib/clock.dart | 21 + runtime/lib/collections.dart | 43 + runtime/lib/date_time.cc | 155 + runtime/lib/date_time.dart | 424 + runtime/lib/double.cc | 212 + runtime/lib/double.dart | 200 + runtime/lib/error.cc | 288 + runtime/lib/error.dart | 55 + runtime/lib/error.h | 19 + runtime/lib/growable_array.dart | 182 + runtime/lib/immutable_map.dart | 108 + runtime/lib/integers.cc | 586 ++ runtime/lib/integers.dart | 182 + runtime/lib/isolate.cc | 378 + runtime/lib/isolate.dart | 166 + runtime/lib/lib_impl_sources.gypi | 34 + runtime/lib/lib_sources.gypi | 17 + runtime/lib/math.cc | 183 + runtime/lib/math.dart | 78 + runtime/lib/object.cc | 35 + runtime/lib/object.dart | 17 + runtime/lib/regexp.cc | 65 + runtime/lib/regexp.dart | 101 + runtime/lib/regexp_jsc.cc | 187 + runtime/lib/regexp_jsc.h | 23 + runtime/lib/string.cc | 150 + runtime/lib/string.dart | 483 ++ runtime/lib/string_buffer.dart | 84 + runtime/tests/dart/dart.status | 42 + runtime/tests/dart/src/BigIntegerTest.dart | 174 + .../src/EchoServerStreamReadUntilTest.dart | 185 + .../tests/dart/src/EchoServerStreamTest.dart | 192 + runtime/tests/dart/src/EchoServerTest.dart | 239 + .../tests/dart/src/FileInputStreamTest.dart | 39 + runtime/tests/dart/src/FileTest.dart | 394 + .../dart/src/GrowableObjectArray2Test.dart | 72 + .../dart/src/GrowableObjectArrayTest.dart | 90 + .../tests/dart/src/ManyEchoServerTest.dart | 17 + runtime/tests/dart/src/MediumIntegerTest.dart | 152 + runtime/tests/dart/src/MultipleTimerTest.dart | 73 + runtime/tests/dart/src/ProcessExitTest.dart | 29 + .../tests/dart/src/ProcessSegfaultTest.dart | 27 + .../dart/src/ProcessStartExceptionTest.dart | 39 + runtime/tests/dart/src/ProcessStderrTest.dart | 53 + runtime/tests/dart/src/ProcessStdoutTest.dart | 55 + runtime/tests/dart/src/SocketCloseTest.dart | 172 + .../tests/dart/src/SocketExceptionTest.dart | 188 + runtime/tests/dart/src/StringBaseTest.dart | 95 + runtime/tests/dart/src/TimerCancelTest.dart | 41 + runtime/tests/dart/src/TimerRepeatTest.dart | 39 + runtime/tests/dart/src/TimerTest.dart | 41 + runtime/tests/dart/src/readuntil_test.dat | 1 + runtime/tests/dart/testcfg.py | 8 + runtime/tests/vm/data/fixed_length_file | 1 + runtime/tests/vm/testcfg.py | 69 + runtime/tests/vm/vm.status | 6 + runtime/third_party/jscre/ASCIICType.h | 151 + runtime/third_party/jscre/AUTHORS | 12 + runtime/third_party/jscre/COPYING | 35 + runtime/third_party/jscre/LICENSE | 85 + runtime/third_party/jscre/config.h | 185 + runtime/third_party/jscre/jscre.gypi | 42 + runtime/third_party/jscre/pcre.h | 93 + runtime/third_party/jscre/pcre_chartables.c | 96 + runtime/third_party/jscre/pcre_compile.cpp | 2677 +++++++ runtime/third_party/jscre/pcre_exec.cpp | 2085 +++++ runtime/third_party/jscre/pcre_internal.h | 428 + runtime/third_party/jscre/pcre_tables.cpp | 75 + .../jscre/pcre_ucp_searchfuncs.cpp | 102 + runtime/third_party/jscre/pcre_xclass.cpp | 118 + runtime/third_party/jscre/ucpinternal.h | 130 + runtime/third_party/jscre/ucptable.cpp | 2968 +++++++ runtime/tools/benchmark.py | 130 + runtime/tools/create_snapshot_file.py | 107 + runtime/tools/create_string_literal.py | 78 + runtime/tools/gdb-macros | 624 ++ runtime/tools/generate_projects.py | 29 + runtime/tools/gyp/runtime-configurations.gypi | 16 + runtime/tools/utils.py | 148 + runtime/tools/valgrind.py | 66 + runtime/vm/allocation.cc | 38 + runtime/vm/allocation.h | 89 + runtime/vm/allocation_test.cc | 157 + runtime/vm/assembler.cc | 169 + runtime/vm/assembler.h | 208 + runtime/vm/assembler_arm.h | 119 + runtime/vm/assembler_ia32.cc | 1463 ++++ runtime/vm/assembler_ia32.h | 623 ++ runtime/vm/assembler_ia32_test.cc | 1730 +++++ runtime/vm/assembler_macros.h | 18 + runtime/vm/assembler_macros_ia32.cc | 57 + runtime/vm/assembler_macros_ia32.h | 40 + runtime/vm/assembler_x64.h | 119 + runtime/vm/assert.cc | 48 + runtime/vm/assert.h | 261 + runtime/vm/assert_test.cc | 48 + runtime/vm/ast.cc | 451 ++ runtime/vm/ast.h | 1918 +++++ runtime/vm/ast_printer.cc | 485 ++ runtime/vm/ast_printer.h | 42 + runtime/vm/ast_printer_test.cc | 39 + runtime/vm/ast_test.cc | 64 + runtime/vm/bigint_operations.cc | 558 ++ runtime/vm/bigint_operations.h | 94 + runtime/vm/bigint_operations_test.cc | 2009 +++++ runtime/vm/bigint_store.h | 36 + runtime/vm/bitfield.h | 65 + runtime/vm/bitfield_test.cc | 24 + runtime/vm/boolfield.h | 39 + runtime/vm/boolfield_test.cc | 21 + runtime/vm/bootstrap.cc | 99 + runtime/vm/bootstrap.h | 31 + runtime/vm/bootstrap_natives.h | 106 + runtime/vm/c99_support_win.h | 52 + runtime/vm/class_finalizer.cc | 933 +++ runtime/vm/class_finalizer.h | 78 + runtime/vm/class_finalizer_test.cc | 82 + runtime/vm/code_generator.cc | 996 +++ runtime/vm/code_generator.h | 93 + runtime/vm/code_generator_arm.h | 50 + runtime/vm/code_generator_ia32.cc | 2578 ++++++ runtime/vm/code_generator_ia32.h | 157 + runtime/vm/code_generator_ia32_test.cc | 499 ++ runtime/vm/code_generator_x64.h | 50 + runtime/vm/code_index_table.cc | 267 + runtime/vm/code_index_table.h | 170 + runtime/vm/code_index_table_test.cc | 157 + runtime/vm/code_patcher.h | 60 + runtime/vm/code_patcher_arm.cc | 65 + runtime/vm/code_patcher_ia32.cc | 258 + runtime/vm/code_patcher_ia32_test.cc | 84 + runtime/vm/code_patcher_x64.cc | 65 + runtime/vm/compiler.cc | 273 + runtime/vm/compiler.h | 47 + runtime/vm/compiler_stats.cc | 65 + runtime/vm/compiler_stats.h | 38 + runtime/vm/compiler_test.cc | 63 + runtime/vm/constants_arm.h | 61 + runtime/vm/constants_ia32.h | 124 + runtime/vm/constants_x64.h | 33 + runtime/vm/corelib_impl_in.cc | 16 + runtime/vm/corelib_in.cc | 15 + runtime/vm/cpu.h | 35 + runtime/vm/cpu_arm.cc | 58 + runtime/vm/cpu_ia32.cc | 128 + runtime/vm/cpu_test.cc | 24 + runtime/vm/cpu_x64.cc | 42 + runtime/vm/dart.cc | 92 + runtime/vm/dart.h | 39 + runtime/vm/dart_api_impl.cc | 1599 ++++ runtime/vm/dart_api_impl.h | 52 + runtime/vm/dart_api_impl_test.cc | 1368 ++++ runtime/vm/dart_api_state.h | 328 + runtime/vm/dart_entry.cc | 173 + runtime/vm/dart_entry.h | 65 + runtime/vm/dart_entry_test.cc | 44 + runtime/vm/debuginfo.h | 103 + runtime/vm/debuginfo_linux.cc | 549 ++ runtime/vm/debuginfo_macos.cc | 50 + runtime/vm/debuginfo_win.cc | 50 + runtime/vm/disassembler.cc | 85 + runtime/vm/disassembler.h | 101 + runtime/vm/disassembler_arm.cc | 29 + runtime/vm/disassembler_ia32.cc | 1514 ++++ runtime/vm/disassembler_test.cc | 27 + runtime/vm/disassembler_x64.cc | 29 + runtime/vm/double_internals.h | 80 + runtime/vm/exceptions.cc | 162 + runtime/vm/exceptions.h | 48 + runtime/vm/exceptions_test.cc | 141 + runtime/vm/flags.cc | 268 + runtime/vm/flags.h | 72 + runtime/vm/flags_test.cc | 54 + runtime/vm/gdbjit_linux.cc | 77 + runtime/vm/gdbjit_linux.h | 13 + runtime/vm/globals.h | 327 + runtime/vm/growable_array.h | 133 + runtime/vm/growable_array_test.cc | 86 + runtime/vm/handles.cc | 115 + runtime/vm/handles.h | 307 + runtime/vm/handles_impl.h | 310 + runtime/vm/handles_test.cc | 82 + runtime/vm/heap.cc | 140 + runtime/vm/heap.h | 106 + runtime/vm/heap_test.cc | 5 + runtime/vm/ic_stubs.cc | 137 + runtime/vm/ic_stubs.h | 114 + runtime/vm/ic_stubs_arm.cc | 41 + runtime/vm/ic_stubs_ia32.cc | 380 + runtime/vm/ic_stubs_ia32_test.cc | 276 + runtime/vm/ic_stubs_x64.cc | 42 + runtime/vm/instructions.h | 20 + runtime/vm/instructions_ia32.cc | 112 + runtime/vm/instructions_ia32.h | 252 + runtime/vm/instructions_ia32_test.cc | 156 + runtime/vm/intrinsifier.h | 27 + runtime/vm/intrinsifier_arm.cc | 18 + runtime/vm/intrinsifier_ia32.cc | 880 +++ runtime/vm/intrinsifier_x64.cc | 18 + runtime/vm/inttypes_support_win.h | 17 + runtime/vm/isolate.cc | 250 + runtime/vm/isolate.h | 270 + runtime/vm/isolate_linux.cc | 47 + runtime/vm/isolate_linux.h | 27 + runtime/vm/isolate_macos.cc | 47 + runtime/vm/isolate_macos.h | 27 + runtime/vm/isolate_test.cc | 54 + runtime/vm/isolate_win.cc | 38 + runtime/vm/isolate_win.h | 27 + runtime/vm/longjump.cc | 43 + runtime/vm/longjump.h | 30 + runtime/vm/longjump_test.cc | 25 + runtime/vm/memory_region.cc | 17 + runtime/vm/memory_region.h | 85 + runtime/vm/memory_region_test.cc | 82 + runtime/vm/native_arguments.cc | 16 + runtime/vm/native_arguments.h | 57 + runtime/vm/native_entry.cc | 33 + runtime/vm/native_entry.h | 67 + runtime/vm/native_entry_test.cc | 90 + runtime/vm/native_entry_test.h | 28 + runtime/vm/object.cc | 6314 +++++++++++++++ runtime/vm/object.h | 2736 +++++++ runtime/vm/object_arm_test.cc | 47 + runtime/vm/object_ia32_test.cc | 52 + runtime/vm/object_store.cc | 161 + runtime/vm/object_store.h | 269 + runtime/vm/object_store_test.cc | 5 + runtime/vm/object_test.cc | 1733 +++++ runtime/vm/object_x64_test.cc | 47 + runtime/vm/opt_code_generator.h | 22 + runtime/vm/opt_code_generator_arm.h | 34 + runtime/vm/opt_code_generator_ia32.cc | 2118 +++++ runtime/vm/opt_code_generator_ia32.h | 122 + runtime/vm/opt_code_generator_x64.h | 34 + runtime/vm/os.h | 104 + runtime/vm/os_linux.cc | 228 + runtime/vm/os_macos.cc | 201 + runtime/vm/os_test.cc | 49 + runtime/vm/os_win.cc | 208 + runtime/vm/pages.cc | 177 + runtime/vm/pages.h | 115 + runtime/vm/pages_test.cc | 34 + runtime/vm/parser.cc | 6904 +++++++++++++++++ runtime/vm/parser.h | 443 ++ runtime/vm/parser_test.cc | 175 + runtime/vm/port.cc | 313 + runtime/vm/port.h | 124 + runtime/vm/port_test.cc | 131 + runtime/vm/random.cc | 28 + runtime/vm/random.h | 23 + runtime/vm/random_test.cc | 16 + runtime/vm/raw_object.cc | 485 ++ runtime/vm/raw_object.h | 748 ++ runtime/vm/raw_object_snapshot.cc | 1284 +++ runtime/vm/resolver.cc | 163 + runtime/vm/resolver.h | 65 + runtime/vm/resolver_test.cc | 225 + runtime/vm/runtime_entry.h | 74 + runtime/vm/runtime_entry_arm.cc | 22 + runtime/vm/runtime_entry_ia32.cc | 43 + runtime/vm/runtime_entry_test.cc | 25 + runtime/vm/runtime_entry_x64.cc | 22 + runtime/vm/scanner.cc | 833 ++ runtime/vm/scanner.h | 200 + runtime/vm/scanner_test.cc | 349 + runtime/vm/scavenger.cc | 219 + runtime/vm/scavenger.h | 101 + runtime/vm/scopes.cc | 413 + runtime/vm/scopes.h | 280 + runtime/vm/scopes_test.cc | 113 + runtime/vm/snapshot.cc | 436 ++ runtime/vm/snapshot.h | 455 ++ runtime/vm/snapshot_test.cc | 467 ++ runtime/vm/snapshot_test.dart | 2198 ++++++ runtime/vm/snapshot_test_in.dat | 1 + runtime/vm/stack_frame.cc | 188 + runtime/vm/stack_frame.h | 264 + runtime/vm/stack_frame_arm.cc | 47 + runtime/vm/stack_frame_ia32.cc | 70 + runtime/vm/stack_frame_test.cc | 303 + runtime/vm/stack_frame_x64.cc | 47 + runtime/vm/store_buffer.cc | 16 + runtime/vm/store_buffer.h | 44 + runtime/vm/stub_code.cc | 211 + runtime/vm/stub_code.h | 176 + runtime/vm/stub_code_arm.cc | 83 + runtime/vm/stub_code_ia32.cc | 1402 ++++ runtime/vm/stub_code_ia32_test.cc | 133 + runtime/vm/stub_code_x64.cc | 83 + runtime/vm/thread.h | 142 + runtime/vm/thread_linux.cc | 241 + runtime/vm/thread_linux.h | 65 + runtime/vm/thread_macos.cc | 229 + runtime/vm/thread_macos.h | 65 + runtime/vm/thread_test.cc | 61 + runtime/vm/thread_win.cc | 172 + runtime/vm/thread_win.h | 61 + runtime/vm/timer.cc | 36 + runtime/vm/timer.h | 159 + runtime/vm/token.cc | 38 + runtime/vm/token.h | 262 + runtime/vm/unit_test.cc | 186 + runtime/vm/unit_test.h | 252 + runtime/vm/utils.cc | 91 + runtime/vm/utils.h | 154 + runtime/vm/utils_test.cc | 170 + runtime/vm/verifier.cc | 42 + runtime/vm/verifier.h | 39 + runtime/vm/virtual_memory.cc | 40 + runtime/vm/virtual_memory.h | 83 + runtime/vm/virtual_memory_linux.cc | 74 + runtime/vm/virtual_memory_macos.cc | 74 + runtime/vm/virtual_memory_test.cc | 47 + runtime/vm/virtual_memory_win.cc | 61 + runtime/vm/visitor.h | 33 + runtime/vm/vm.gypi | 203 + runtime/vm/vm_sources.gypi | 243 + runtime/vm/zone.cc | 247 + runtime/vm/zone.h | 166 + runtime/vm/zone_test.cc | 122 + 394 files changed, 97455 insertions(+) create mode 100644 runtime/PRESUBMIT.py create mode 100644 runtime/bin/bin.gypi create mode 100644 runtime/bin/builtin.cc create mode 100644 runtime/bin/builtin.dart create mode 100644 runtime/bin/builtin.h create mode 100644 runtime/bin/builtin_in.cc create mode 100644 runtime/bin/dartutils.cc create mode 100644 runtime/bin/dartutils.h create mode 100644 runtime/bin/directory.cc create mode 100644 runtime/bin/directory.dart create mode 100644 runtime/bin/directory.h create mode 100644 runtime/bin/directory_impl.dart create mode 100644 runtime/bin/directory_linux.cc create mode 100644 runtime/bin/directory_macos.cc create mode 100644 runtime/bin/directory_win.cc create mode 100644 runtime/bin/eventhandler.cc create mode 100644 runtime/bin/eventhandler.dart create mode 100644 runtime/bin/eventhandler.h create mode 100644 runtime/bin/eventhandler_linux.cc create mode 100644 runtime/bin/eventhandler_linux.h create mode 100644 runtime/bin/eventhandler_macos.cc create mode 100644 runtime/bin/eventhandler_macos.h create mode 100644 runtime/bin/eventhandler_win.cc create mode 100644 runtime/bin/eventhandler_win.h create mode 100644 runtime/bin/fdutils.h create mode 100644 runtime/bin/fdutils_linux.cc create mode 100644 runtime/bin/fdutils_macos.cc create mode 100644 runtime/bin/file.cc create mode 100644 runtime/bin/file.dart create mode 100644 runtime/bin/file.h create mode 100644 runtime/bin/file_impl.dart create mode 100644 runtime/bin/file_linux.cc create mode 100644 runtime/bin/file_macos.cc create mode 100644 runtime/bin/file_test.cc create mode 100644 runtime/bin/file_win.cc create mode 100644 runtime/bin/gen_snapshot.cc create mode 100644 runtime/bin/globals.h create mode 100644 runtime/bin/input_stream.dart create mode 100644 runtime/bin/main.cc create mode 100644 runtime/bin/output_stream.dart create mode 100644 runtime/bin/process.cc create mode 100644 runtime/bin/process.dart create mode 100644 runtime/bin/process.h create mode 100644 runtime/bin/process_impl.dart create mode 100644 runtime/bin/process_linux.cc create mode 100644 runtime/bin/process_macos.cc create mode 100644 runtime/bin/process_script.cc create mode 100644 runtime/bin/process_script.h create mode 100644 runtime/bin/process_test.cc create mode 100644 runtime/bin/process_win.cc create mode 100644 runtime/bin/run_vm_tests.cc create mode 100644 runtime/bin/set.h create mode 100644 runtime/bin/set_test.cc create mode 100644 runtime/bin/snapshot_empty.cc create mode 100644 runtime/bin/snapshot_in.cc create mode 100644 runtime/bin/socket.cc create mode 100644 runtime/bin/socket.dart create mode 100644 runtime/bin/socket.h create mode 100644 runtime/bin/socket_impl.dart create mode 100644 runtime/bin/socket_linux.cc create mode 100644 runtime/bin/socket_macos.cc create mode 100644 runtime/bin/socket_stream.dart create mode 100644 runtime/bin/socket_win.cc create mode 100644 runtime/bin/timer.dart create mode 100644 runtime/bin/timer_impl.dart create mode 100644 runtime/codereview.settings create mode 100644 runtime/dart-runtime.gyp create mode 100755 runtime/include/dart_api.h create mode 100644 runtime/lib/array.cc create mode 100644 runtime/lib/array.dart create mode 100644 runtime/lib/arrays.dart create mode 100644 runtime/lib/bool.dart create mode 100644 runtime/lib/clock.cc create mode 100644 runtime/lib/clock.dart create mode 100644 runtime/lib/collections.dart create mode 100644 runtime/lib/date_time.cc create mode 100644 runtime/lib/date_time.dart create mode 100644 runtime/lib/double.cc create mode 100644 runtime/lib/double.dart create mode 100644 runtime/lib/error.cc create mode 100644 runtime/lib/error.dart create mode 100644 runtime/lib/error.h create mode 100644 runtime/lib/growable_array.dart create mode 100644 runtime/lib/immutable_map.dart create mode 100644 runtime/lib/integers.cc create mode 100644 runtime/lib/integers.dart create mode 100644 runtime/lib/isolate.cc create mode 100644 runtime/lib/isolate.dart create mode 100644 runtime/lib/lib_impl_sources.gypi create mode 100644 runtime/lib/lib_sources.gypi create mode 100644 runtime/lib/math.cc create mode 100644 runtime/lib/math.dart create mode 100644 runtime/lib/object.cc create mode 100644 runtime/lib/object.dart create mode 100644 runtime/lib/regexp.cc create mode 100644 runtime/lib/regexp.dart create mode 100644 runtime/lib/regexp_jsc.cc create mode 100644 runtime/lib/regexp_jsc.h create mode 100644 runtime/lib/string.cc create mode 100644 runtime/lib/string.dart create mode 100644 runtime/lib/string_buffer.dart create mode 100644 runtime/tests/dart/dart.status create mode 100644 runtime/tests/dart/src/BigIntegerTest.dart create mode 100644 runtime/tests/dart/src/EchoServerStreamReadUntilTest.dart create mode 100644 runtime/tests/dart/src/EchoServerStreamTest.dart create mode 100644 runtime/tests/dart/src/EchoServerTest.dart create mode 100644 runtime/tests/dart/src/FileInputStreamTest.dart create mode 100644 runtime/tests/dart/src/FileTest.dart create mode 100644 runtime/tests/dart/src/GrowableObjectArray2Test.dart create mode 100644 runtime/tests/dart/src/GrowableObjectArrayTest.dart create mode 100644 runtime/tests/dart/src/ManyEchoServerTest.dart create mode 100644 runtime/tests/dart/src/MediumIntegerTest.dart create mode 100644 runtime/tests/dart/src/MultipleTimerTest.dart create mode 100644 runtime/tests/dart/src/ProcessExitTest.dart create mode 100644 runtime/tests/dart/src/ProcessSegfaultTest.dart create mode 100644 runtime/tests/dart/src/ProcessStartExceptionTest.dart create mode 100644 runtime/tests/dart/src/ProcessStderrTest.dart create mode 100644 runtime/tests/dart/src/ProcessStdoutTest.dart create mode 100644 runtime/tests/dart/src/SocketCloseTest.dart create mode 100644 runtime/tests/dart/src/SocketExceptionTest.dart create mode 100644 runtime/tests/dart/src/StringBaseTest.dart create mode 100644 runtime/tests/dart/src/TimerCancelTest.dart create mode 100644 runtime/tests/dart/src/TimerRepeatTest.dart create mode 100644 runtime/tests/dart/src/TimerTest.dart create mode 100644 runtime/tests/dart/src/readuntil_test.dat create mode 100644 runtime/tests/dart/testcfg.py create mode 100644 runtime/tests/vm/data/fixed_length_file create mode 100644 runtime/tests/vm/testcfg.py create mode 100644 runtime/tests/vm/vm.status create mode 100644 runtime/third_party/jscre/ASCIICType.h create mode 100644 runtime/third_party/jscre/AUTHORS create mode 100644 runtime/third_party/jscre/COPYING create mode 100644 runtime/third_party/jscre/LICENSE create mode 100644 runtime/third_party/jscre/config.h create mode 100644 runtime/third_party/jscre/jscre.gypi create mode 100644 runtime/third_party/jscre/pcre.h create mode 100644 runtime/third_party/jscre/pcre_chartables.c create mode 100644 runtime/third_party/jscre/pcre_compile.cpp create mode 100644 runtime/third_party/jscre/pcre_exec.cpp create mode 100644 runtime/third_party/jscre/pcre_internal.h create mode 100644 runtime/third_party/jscre/pcre_tables.cpp create mode 100644 runtime/third_party/jscre/pcre_ucp_searchfuncs.cpp create mode 100644 runtime/third_party/jscre/pcre_xclass.cpp create mode 100644 runtime/third_party/jscre/ucpinternal.h create mode 100644 runtime/third_party/jscre/ucptable.cpp create mode 100755 runtime/tools/benchmark.py create mode 100755 runtime/tools/create_snapshot_file.py create mode 100644 runtime/tools/create_string_literal.py create mode 100644 runtime/tools/gdb-macros create mode 100755 runtime/tools/generate_projects.py create mode 100644 runtime/tools/gyp/runtime-configurations.gypi create mode 100644 runtime/tools/utils.py create mode 100755 runtime/tools/valgrind.py create mode 100644 runtime/vm/allocation.cc create mode 100644 runtime/vm/allocation.h create mode 100644 runtime/vm/allocation_test.cc create mode 100644 runtime/vm/assembler.cc create mode 100644 runtime/vm/assembler.h create mode 100644 runtime/vm/assembler_arm.h create mode 100644 runtime/vm/assembler_ia32.cc create mode 100644 runtime/vm/assembler_ia32.h create mode 100644 runtime/vm/assembler_ia32_test.cc create mode 100644 runtime/vm/assembler_macros.h create mode 100644 runtime/vm/assembler_macros_ia32.cc create mode 100644 runtime/vm/assembler_macros_ia32.h create mode 100644 runtime/vm/assembler_x64.h create mode 100644 runtime/vm/assert.cc create mode 100644 runtime/vm/assert.h create mode 100644 runtime/vm/assert_test.cc create mode 100644 runtime/vm/ast.cc create mode 100644 runtime/vm/ast.h create mode 100644 runtime/vm/ast_printer.cc create mode 100644 runtime/vm/ast_printer.h create mode 100644 runtime/vm/ast_printer_test.cc create mode 100644 runtime/vm/ast_test.cc create mode 100644 runtime/vm/bigint_operations.cc create mode 100644 runtime/vm/bigint_operations.h create mode 100644 runtime/vm/bigint_operations_test.cc create mode 100644 runtime/vm/bigint_store.h create mode 100644 runtime/vm/bitfield.h create mode 100644 runtime/vm/bitfield_test.cc create mode 100644 runtime/vm/boolfield.h create mode 100644 runtime/vm/boolfield_test.cc create mode 100644 runtime/vm/bootstrap.cc create mode 100644 runtime/vm/bootstrap.h create mode 100644 runtime/vm/bootstrap_natives.h create mode 100644 runtime/vm/c99_support_win.h create mode 100644 runtime/vm/class_finalizer.cc create mode 100644 runtime/vm/class_finalizer.h create mode 100644 runtime/vm/class_finalizer_test.cc create mode 100644 runtime/vm/code_generator.cc create mode 100644 runtime/vm/code_generator.h create mode 100644 runtime/vm/code_generator_arm.h create mode 100644 runtime/vm/code_generator_ia32.cc create mode 100644 runtime/vm/code_generator_ia32.h create mode 100644 runtime/vm/code_generator_ia32_test.cc create mode 100644 runtime/vm/code_generator_x64.h create mode 100644 runtime/vm/code_index_table.cc create mode 100644 runtime/vm/code_index_table.h create mode 100644 runtime/vm/code_index_table_test.cc create mode 100644 runtime/vm/code_patcher.h create mode 100644 runtime/vm/code_patcher_arm.cc create mode 100644 runtime/vm/code_patcher_ia32.cc create mode 100644 runtime/vm/code_patcher_ia32_test.cc create mode 100644 runtime/vm/code_patcher_x64.cc create mode 100644 runtime/vm/compiler.cc create mode 100644 runtime/vm/compiler.h create mode 100644 runtime/vm/compiler_stats.cc create mode 100644 runtime/vm/compiler_stats.h create mode 100644 runtime/vm/compiler_test.cc create mode 100644 runtime/vm/constants_arm.h create mode 100644 runtime/vm/constants_ia32.h create mode 100644 runtime/vm/constants_x64.h create mode 100644 runtime/vm/corelib_impl_in.cc create mode 100644 runtime/vm/corelib_in.cc create mode 100644 runtime/vm/cpu.h create mode 100644 runtime/vm/cpu_arm.cc create mode 100644 runtime/vm/cpu_ia32.cc create mode 100644 runtime/vm/cpu_test.cc create mode 100644 runtime/vm/cpu_x64.cc create mode 100644 runtime/vm/dart.cc create mode 100644 runtime/vm/dart.h create mode 100644 runtime/vm/dart_api_impl.cc create mode 100644 runtime/vm/dart_api_impl.h create mode 100644 runtime/vm/dart_api_impl_test.cc create mode 100644 runtime/vm/dart_api_state.h create mode 100644 runtime/vm/dart_entry.cc create mode 100644 runtime/vm/dart_entry.h create mode 100644 runtime/vm/dart_entry_test.cc create mode 100644 runtime/vm/debuginfo.h create mode 100644 runtime/vm/debuginfo_linux.cc create mode 100644 runtime/vm/debuginfo_macos.cc create mode 100644 runtime/vm/debuginfo_win.cc create mode 100644 runtime/vm/disassembler.cc create mode 100644 runtime/vm/disassembler.h create mode 100644 runtime/vm/disassembler_arm.cc create mode 100644 runtime/vm/disassembler_ia32.cc create mode 100644 runtime/vm/disassembler_test.cc create mode 100644 runtime/vm/disassembler_x64.cc create mode 100644 runtime/vm/double_internals.h create mode 100644 runtime/vm/exceptions.cc create mode 100644 runtime/vm/exceptions.h create mode 100644 runtime/vm/exceptions_test.cc create mode 100644 runtime/vm/flags.cc create mode 100644 runtime/vm/flags.h create mode 100644 runtime/vm/flags_test.cc create mode 100644 runtime/vm/gdbjit_linux.cc create mode 100644 runtime/vm/gdbjit_linux.h create mode 100644 runtime/vm/globals.h create mode 100644 runtime/vm/growable_array.h create mode 100644 runtime/vm/growable_array_test.cc create mode 100644 runtime/vm/handles.cc create mode 100644 runtime/vm/handles.h create mode 100644 runtime/vm/handles_impl.h create mode 100644 runtime/vm/handles_test.cc create mode 100644 runtime/vm/heap.cc create mode 100644 runtime/vm/heap.h create mode 100644 runtime/vm/heap_test.cc create mode 100644 runtime/vm/ic_stubs.cc create mode 100644 runtime/vm/ic_stubs.h create mode 100644 runtime/vm/ic_stubs_arm.cc create mode 100644 runtime/vm/ic_stubs_ia32.cc create mode 100644 runtime/vm/ic_stubs_ia32_test.cc create mode 100644 runtime/vm/ic_stubs_x64.cc create mode 100644 runtime/vm/instructions.h create mode 100644 runtime/vm/instructions_ia32.cc create mode 100644 runtime/vm/instructions_ia32.h create mode 100644 runtime/vm/instructions_ia32_test.cc create mode 100644 runtime/vm/intrinsifier.h create mode 100644 runtime/vm/intrinsifier_arm.cc create mode 100644 runtime/vm/intrinsifier_ia32.cc create mode 100644 runtime/vm/intrinsifier_x64.cc create mode 100644 runtime/vm/inttypes_support_win.h create mode 100644 runtime/vm/isolate.cc create mode 100644 runtime/vm/isolate.h create mode 100644 runtime/vm/isolate_linux.cc create mode 100644 runtime/vm/isolate_linux.h create mode 100644 runtime/vm/isolate_macos.cc create mode 100644 runtime/vm/isolate_macos.h create mode 100644 runtime/vm/isolate_test.cc create mode 100644 runtime/vm/isolate_win.cc create mode 100644 runtime/vm/isolate_win.h create mode 100644 runtime/vm/longjump.cc create mode 100644 runtime/vm/longjump.h create mode 100644 runtime/vm/longjump_test.cc create mode 100644 runtime/vm/memory_region.cc create mode 100644 runtime/vm/memory_region.h create mode 100644 runtime/vm/memory_region_test.cc create mode 100644 runtime/vm/native_arguments.cc create mode 100644 runtime/vm/native_arguments.h create mode 100644 runtime/vm/native_entry.cc create mode 100644 runtime/vm/native_entry.h create mode 100644 runtime/vm/native_entry_test.cc create mode 100644 runtime/vm/native_entry_test.h create mode 100644 runtime/vm/object.cc create mode 100644 runtime/vm/object.h create mode 100644 runtime/vm/object_arm_test.cc create mode 100644 runtime/vm/object_ia32_test.cc create mode 100644 runtime/vm/object_store.cc create mode 100644 runtime/vm/object_store.h create mode 100644 runtime/vm/object_store_test.cc create mode 100644 runtime/vm/object_test.cc create mode 100644 runtime/vm/object_x64_test.cc create mode 100644 runtime/vm/opt_code_generator.h create mode 100644 runtime/vm/opt_code_generator_arm.h create mode 100644 runtime/vm/opt_code_generator_ia32.cc create mode 100644 runtime/vm/opt_code_generator_ia32.h create mode 100644 runtime/vm/opt_code_generator_x64.h create mode 100644 runtime/vm/os.h create mode 100644 runtime/vm/os_linux.cc create mode 100644 runtime/vm/os_macos.cc create mode 100644 runtime/vm/os_test.cc create mode 100644 runtime/vm/os_win.cc create mode 100644 runtime/vm/pages.cc create mode 100644 runtime/vm/pages.h create mode 100644 runtime/vm/pages_test.cc create mode 100644 runtime/vm/parser.cc create mode 100644 runtime/vm/parser.h create mode 100644 runtime/vm/parser_test.cc create mode 100644 runtime/vm/port.cc create mode 100644 runtime/vm/port.h create mode 100644 runtime/vm/port_test.cc create mode 100644 runtime/vm/random.cc create mode 100644 runtime/vm/random.h create mode 100644 runtime/vm/random_test.cc create mode 100644 runtime/vm/raw_object.cc create mode 100644 runtime/vm/raw_object.h create mode 100644 runtime/vm/raw_object_snapshot.cc create mode 100644 runtime/vm/resolver.cc create mode 100644 runtime/vm/resolver.h create mode 100644 runtime/vm/resolver_test.cc create mode 100644 runtime/vm/runtime_entry.h create mode 100644 runtime/vm/runtime_entry_arm.cc create mode 100644 runtime/vm/runtime_entry_ia32.cc create mode 100644 runtime/vm/runtime_entry_test.cc create mode 100644 runtime/vm/runtime_entry_x64.cc create mode 100644 runtime/vm/scanner.cc create mode 100644 runtime/vm/scanner.h create mode 100644 runtime/vm/scanner_test.cc create mode 100644 runtime/vm/scavenger.cc create mode 100644 runtime/vm/scavenger.h create mode 100644 runtime/vm/scopes.cc create mode 100644 runtime/vm/scopes.h create mode 100644 runtime/vm/scopes_test.cc create mode 100644 runtime/vm/snapshot.cc create mode 100644 runtime/vm/snapshot.h create mode 100644 runtime/vm/snapshot_test.cc create mode 100644 runtime/vm/snapshot_test.dart create mode 100644 runtime/vm/snapshot_test_in.dat create mode 100644 runtime/vm/stack_frame.cc create mode 100644 runtime/vm/stack_frame.h create mode 100644 runtime/vm/stack_frame_arm.cc create mode 100644 runtime/vm/stack_frame_ia32.cc create mode 100644 runtime/vm/stack_frame_test.cc create mode 100644 runtime/vm/stack_frame_x64.cc create mode 100644 runtime/vm/store_buffer.cc create mode 100644 runtime/vm/store_buffer.h create mode 100644 runtime/vm/stub_code.cc create mode 100644 runtime/vm/stub_code.h create mode 100644 runtime/vm/stub_code_arm.cc create mode 100644 runtime/vm/stub_code_ia32.cc create mode 100644 runtime/vm/stub_code_ia32_test.cc create mode 100644 runtime/vm/stub_code_x64.cc create mode 100644 runtime/vm/thread.h create mode 100644 runtime/vm/thread_linux.cc create mode 100644 runtime/vm/thread_linux.h create mode 100644 runtime/vm/thread_macos.cc create mode 100644 runtime/vm/thread_macos.h create mode 100644 runtime/vm/thread_test.cc create mode 100644 runtime/vm/thread_win.cc create mode 100644 runtime/vm/thread_win.h create mode 100644 runtime/vm/timer.cc create mode 100644 runtime/vm/timer.h create mode 100644 runtime/vm/token.cc create mode 100644 runtime/vm/token.h create mode 100644 runtime/vm/unit_test.cc create mode 100644 runtime/vm/unit_test.h create mode 100644 runtime/vm/utils.cc create mode 100644 runtime/vm/utils.h create mode 100644 runtime/vm/utils_test.cc create mode 100644 runtime/vm/verifier.cc create mode 100644 runtime/vm/verifier.h create mode 100644 runtime/vm/virtual_memory.cc create mode 100644 runtime/vm/virtual_memory.h create mode 100644 runtime/vm/virtual_memory_linux.cc create mode 100644 runtime/vm/virtual_memory_macos.cc create mode 100644 runtime/vm/virtual_memory_test.cc create mode 100644 runtime/vm/virtual_memory_win.cc create mode 100644 runtime/vm/visitor.h create mode 100644 runtime/vm/vm.gypi create mode 100644 runtime/vm/vm_sources.gypi create mode 100644 runtime/vm/zone.cc create mode 100644 runtime/vm/zone.h create mode 100644 runtime/vm/zone_test.cc diff --git a/runtime/PRESUBMIT.py b/runtime/PRESUBMIT.py new file mode 100644 index 00000000000..c3bf724daae --- /dev/null +++ b/runtime/PRESUBMIT.py @@ -0,0 +1,46 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +import cpplint +import os + +def RunLint(input_api, output_api): + result = [] + cpplint._cpplint_state.ResetErrorCounts() + # Find all .cc and .h files in the change list. + for svn_file in input_api.AffectedTextFiles(): + filename = svn_file.AbsoluteLocalPath() + if filename.endswith('.cc') or filename.endswith('.h'): + hacked_parent_svn = 0 + if filename.endswith('.h'): + parent_path = os.path.dirname(input_api.PresubmitLocalPath()) + orig_path = os.path.join(parent_path, '.svn') + renamed_path = os.path.join(parent_path, '.svn_orig') + if (os.path.exists(renamed_path)): + error_msg = '".svn_orig" exists in presubmit parent directory(' + error_msg += parent_path + error_msg += '). Consider renaming it manually to ".svn".' + result = [output_api.PresubmitError(error_msg)] + return result + if (os.path.exists(orig_path)): + # Make the parent SVN directory non-discoverable by cpplint to get + # the correct header guard checks. This is needed if using all Dart + # checkout. + os.rename(orig_path, renamed_path) + hacked_parent_svn = 1 + # Run cpplint on the file. + cpplint.ProcessFile(filename, 1) + if hacked_parent_svn != 0: + # Undo hacks from above: Restore the original name. + os.rename(renamed_path, orig_path) + # Report a presubmit error if any of the files had an error. + if cpplint._cpplint_state.error_count > 0: + result = [output_api.PresubmitError('Failed cpplint check.')] + return result + +def CheckChangeOnUpload(input_api, output_api): + return RunLint(input_api, output_api) + +def CheckChangeOnCommit(input_api, output_api): + return RunLint(input_api, output_api) diff --git a/runtime/bin/bin.gypi b/runtime/bin/bin.gypi new file mode 100644 index 00000000000..7dd70b60c1b --- /dev/null +++ b/runtime/bin/bin.gypi @@ -0,0 +1,411 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +{ + 'variables': { + 'builtin_in_cc_file': 'builtin_in.cc', + 'builtin_cc_file': '<(SHARED_INTERMEDIATE_DIR)/builtin_gen.cc', + 'snapshot_in_cc_file': 'snapshot_in.cc', + 'snapshot_bin_file': '<(SHARED_INTERMEDIATE_DIR)/snapshot_gen.bin', + 'snapshot_cc_file': '<(SHARED_INTERMEDIATE_DIR)/snapshot_gen.cc', + }, + 'targets': [ + { + 'target_name': 'generate_builtin_cc_file', + 'type': 'none', + 'conditions': [ + ['OS=="win"', { + 'msvs_cygwin_dirs': ['<(DEPTH)/../third_party/cygwin'], + }], + ], + 'sources': [ + 'builtin.dart', + 'directory.dart', + 'directory_impl.dart', + 'eventhandler.dart', + 'file.dart', + 'file_impl.dart', + 'input_stream.dart', + 'output_stream.dart', + 'process.dart', + 'process_impl.dart', + 'socket.dart', + 'socket_impl.dart', + 'socket_stream.dart', + 'timer.dart', + 'timer_impl.dart', + ], + 'actions': [ + { + 'action_name': 'generate_builtin_cc', + 'inputs': [ + '../tools/create_string_literal.py', + '<(builtin_in_cc_file)', + '<@(_sources)', + ], + 'outputs': [ + '<(builtin_cc_file)', + ], + 'action': [ + 'python', + 'tools/create_string_literal.py', + '--output', '<(builtin_cc_file)', + '--input_cc', '<(builtin_in_cc_file)', + '<@(_sources)', + ], + 'message': 'Generating ''<(builtin_cc_file)'' file.' + }, + ] + }, + { + 'target_name': 'libdart_builtin', + 'type': 'static_library', + 'dependencies': [ + 'generate_builtin_cc_file', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'builtin.cc', + 'builtin.h', + 'dartutils.h', + 'dartutils.cc', + 'directory.h', + 'directory.cc', + 'directory_linux.cc', + 'directory_macos.cc', + 'directory_win.cc', + 'eventhandler.cc', + 'eventhandler.h', + 'eventhandler_linux.cc', + 'eventhandler_linux.h', + 'eventhandler_macos.cc', + 'eventhandler_macos.h', + 'eventhandler_win.cc', + 'eventhandler_win.h', + 'file.cc', + 'file.h', + 'file_linux.cc', + 'file_macos.cc', + 'file_win.cc', + 'fdutils.h', + 'fdutils_linux.cc', + 'fdutils_macos.cc', + 'globals.h', + 'process.cc', + 'process.h', + 'process_linux.cc', + 'process_macos.cc', + 'process_win.cc', + 'socket.cc', + 'socket.h', + 'socket_linux.cc', + 'socket_macos.cc', + 'socket_win.cc', + 'set.h', + # Include generated source files. + '<(builtin_cc_file)', + ], + 'conditions': [ + ['OS=="win"', {'sources/' : [ + ['exclude', 'fdutils.h'], + ]}], + ], + }, + { + # Standalone executable using the shared libdart library. + 'target_name': 'dart_no_snapshot', + 'type': 'executable', + 'dependencies': [ + 'libdart', + 'libdart_builtin', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'main.cc', + 'process_script.cc', + 'process_script.h', + 'snapshot_empty.cc', + ], + 'conditions': [ + ['OS=="win"', { + 'link_settings': { + 'libraries': [ '-lws2_32.lib' ], + }, + }]], + }, + { + # Completely statically linked dart binary. + 'target_name': 'dart_no_snapshot_bin', + 'type': 'executable', + 'dependencies': [ + 'libdart_lib', + 'libdart_vm', + 'libjscre', + 'libdart_api', + 'libdart_builtin', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'main.cc', + 'process_script.cc', + 'process_script.h', + 'snapshot_empty.cc', + ], + 'conditions': [ + ['OS=="win"', { + 'link_settings': { + 'libraries': [ '-lws2_32.lib' ], + }, + }]], + }, + { + # Completely statically linked binary for generating snapshots. + 'target_name': 'gen_snapshot_bin', + 'type': 'executable', + 'dependencies': [ + 'libdart_lib', + 'libdart_vm', + 'libjscre', + 'libdart_api', + 'libdart_builtin', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'gen_snapshot.cc', + 'process_script.cc', + 'process_script.h', + ], + 'conditions': [ + ['OS=="win"', { + 'link_settings': { + 'libraries': [ '-lws2_32.lib' ], + }, + }]], + }, + { + # Generate snapshot file. + 'target_name': 'generate_snapshot_file', + 'type': 'none', + 'conditions': [ + ['OS=="win"', { + 'msvs_cygwin_dirs': ['<(DEPTH)/../third_party/cygwin'], + }], + ], + 'dependencies': [ + 'gen_snapshot_bin', + ], + 'actions': [ + { + 'action_name': 'generate_snapshot_file', + 'inputs': [ + '../tools/create_snapshot_file.py', + '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)gen_snapshot_bin<(EXECUTABLE_SUFFIX)', + '<(snapshot_in_cc_file)', + ], + 'outputs': [ + '<(snapshot_cc_file)', + ], + 'action': [ + 'python', + 'tools/create_snapshot_file.py', + '--executable', '<(PRODUCT_DIR)/gen_snapshot_bin', + '--output_bin', '<(snapshot_bin_file)', + '--input_cc', '<(snapshot_in_cc_file)', + '--output', '<(snapshot_cc_file)', + ], + 'message': 'Generating ''<(snapshot_cc_file)'' file.' + }, + ] + }, + { + # Standalone executable using the shared libdart library with a snapshot + # of the core and builtin libraries linked in. + 'target_name': 'dart', + 'type': 'executable', + 'dependencies': [ + 'libdart', + 'libdart_builtin', + 'generate_snapshot_file', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'main.cc', + 'process_script.cc', + 'process_script.h', + '<(snapshot_cc_file)', + ], + 'conditions': [ + ['OS=="win"', { + 'link_settings': { + 'libraries': [ '-lws2_32.lib' ], + }, + }]], + }, + { + # Completely statically linked dart binary with a snapshot of the core + # and builtin libraries linked in. + 'target_name': 'dart_bin', + 'type': 'executable', + 'dependencies': [ + 'libdart_lib', + 'libdart_vm', + 'libjscre', + 'libdart_api', + 'libdart_builtin', + 'generate_snapshot_file', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'main.cc', + 'process_script.cc', + 'process_script.h', + '<(snapshot_cc_file)', + ], + 'conditions': [ + ['OS=="win"', { + 'link_settings': { + 'libraries': [ '-lws2_32.lib' ], + }, + }]], + }, + { + 'target_name': 'process_test', + 'type': 'executable', + 'sources': [ + 'process_test.cc', + ] + }, + { + 'target_name': 'run_vm_tests', + 'type': 'executable', + # The unittest framework needs to be able to call the unexported symbols, + # which is why it links against the static libraries. In general binaries + # should depend on the shared library. + 'dependencies': [ + 'libdart_lib', + 'libdart_vm', + 'libjscre', + 'libdart_api', + 'generate_snapshot_test_dat_file', + ], + 'include_dirs': [ + '..', + '<(SHARED_INTERMEDIATE_DIR)', + ], + 'sources': [ + 'run_vm_tests.cc', + 'dartutils.h', + 'dartutils.cc', + 'directory.h', + 'directory.cc', + 'directory_linux.cc', + 'directory_macos.cc', + 'directory_win.cc', + 'eventhandler.cc', + 'eventhandler.h', + 'eventhandler_linux.cc', + 'eventhandler_linux.h', + 'eventhandler_macos.cc', + 'eventhandler_macos.h', + 'eventhandler_win.cc', + 'eventhandler_win.h', + 'file.cc', + 'file.h', + 'file_test.cc', + 'file_linux.cc', + 'file_macos.cc', + 'file_win.cc', + 'fdutils.h', + 'fdutils_linux.cc', + 'fdutils_macos.cc', + 'process.cc', + 'process.h', + 'process_linux.cc', + 'process_macos.cc', + 'process_win.cc', + 'socket.cc', + 'socket.h', + 'socket_linux.cc', + 'socket_macos.cc', + 'socket_win.cc', + 'set.h', + 'set_test.cc', + ], + 'includes': [ + '../vm/vm_sources.gypi', + ], + 'defines': [ + 'TESTING', + ], + # Only include _test.[cc|h] files. + 'sources/': [ + ['exclude', '\\.(cc|h)$'], + ['include', '_test\\.(cc|h)$'], + ['include', 'run_vm_tests.cc'], + ['include', 'dart_api_impl.cc'], + ['include', 'dartutils.h'], + ['include', 'dartutils.cc'], + ['include', 'directory.h'], + ['include', 'directory.cc'], + ['include', 'eventhandler.cc'], + ['include', 'eventhandler.h'], + ['include', 'file.cc'], + ['include', 'file.h'], + ['include', 'file_test.cc'], + ['include', 'process.cc'], + ['include', 'process.h'], + ['include', 'socket.cc'], + ['include', 'socket.h'], + ['include', 'set_test.cc'], + ], + 'conditions': [ + ['OS=="linux"', {'sources/' : [ + ['include', 'directory_linux.cc'], + ['include', 'eventhandler_linux.cc'], + ['include', 'eventhandler_linux.h'], + ['include', 'fdutils.h'], + ['include', 'fdutils_linux.cc'], + ['include', 'file_linux.cc'], + ['include', 'process_linux.cc'], + ['include', 'socket_linux.cc'], + ]}], + ['OS=="mac"', {'sources/' : [ + ['include', 'directory_macos.cc'], + ['include', 'eventhandler_macos.cc'], + ['include', 'eventhandler_macos.h'], + ['include', 'fdutils.h'], + ['include', 'fdutils_macos.cc'], + ['include', 'file_macos.cc'], + ['include', 'process_macos.cc'], + ['include', 'socket_macos.cc'], + ]}], + ['OS=="win"', {'sources/' : [ + ['include', 'directory_win.cc'], + ['include', 'eventhandler_win.cc'], + ['include', 'eventhandler_win.h'], + ['include', 'file_win.cc'], + ['include', 'process_win.cc'], + ['include', 'socket_win.cc'], + ]}], + ['OS=="win"', { + 'link_settings': { + 'libraries': [ '-lws2_32.lib' ], + }, + }], + ], + }, + ], +} diff --git a/runtime/bin/builtin.cc b/runtime/bin/builtin.cc new file mode 100644 index 00000000000..5f8abcf3352 --- /dev/null +++ b/runtime/bin/builtin.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include + +#include "include/dart_api.h" + +#include "bin/builtin.h" + +// Implementation of native functions which are used for some +// test/debug functionality in standalone dart mode. + +void PrintString(FILE* out, Dart_Handle string) { + Dart_Result result = Dart_StringToCString(string); + const char* cstring = Dart_IsValidResult(result) ? + Dart_GetResultAsCString(result) : Dart_GetErrorCString(result); + fprintf(out, "%s\n", cstring); +} + + +void FUNCTION_NAME(Logger_PrintString)(Dart_NativeArguments args) { + Dart_EnterScope(); + PrintString(stdout, Dart_GetNativeArgument(args, 0)); + Dart_ExitScope(); +} diff --git a/runtime/bin/builtin.dart b/runtime/bin/builtin.dart new file mode 100644 index 00000000000..c54c48619e6 --- /dev/null +++ b/runtime/bin/builtin.dart @@ -0,0 +1,13 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#library("builtin"); + +void print(arg) { + _Logger._printString(arg.toString()); +} + +class _Logger { + static void _printString(String s) native "Logger_PrintString"; +} diff --git a/runtime/bin/builtin.h b/runtime/bin/builtin.h new file mode 100644 index 00000000000..f8c19863438 --- /dev/null +++ b/runtime/bin/builtin.h @@ -0,0 +1,42 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_BUILTIN_H_ +#define BIN_BUILTIN_H_ + +#include +#include + +#include "bin/globals.h" +#include "include/dart_api.h" + +#ifdef DEBUG +#define ASSERT(expr) assert(expr) +#else +#define ASSERT(expr) USE(expr) +#endif + +#define FATAL(error) \ + fprintf(stderr, "%s\n", error); \ + fflush(stderr); \ + abort(); + +#define UNREACHABLE() \ + FATAL("unreachable code") + +#define UNIMPLEMENTED() \ + FATAL("unimplemented code") + +#define FUNCTION_NAME(name) Builtin_##name +#define REGISTER_FUNCTION(name, count) \ + { ""#name, FUNCTION_NAME(name), count }, +#define DECLARE_FUNCTION(name, count) \ + extern void FUNCTION_NAME(name)(Dart_NativeArguments args); + +extern void Builtin_LoadLibrary(); +extern void Builtin_ImportLibrary(Dart_Handle library); +extern void Builtin_SetNativeResolver(); +extern void PrintString(FILE* out, Dart_Handle object); + +#endif // BIN_BUILTIN_H_ diff --git a/runtime/bin/builtin_in.cc b/runtime/bin/builtin_in.cc new file mode 100644 index 00000000000..3710a963c7b --- /dev/null +++ b/runtime/bin/builtin_in.cc @@ -0,0 +1,156 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include + +#include "include/dart_api.h" + +#include "bin/builtin.h" +#include "bin/dartutils.h" + +// The string on the next line will be filled in with the contents of the +// builtin.dart file. +// This string forms the content of builtin functionality which is injected +// into standalone dart to provide some test/debug functionality. +static const char Builtin_source_[] = { + %s +}; + + +// List all native functions implemented in standalone dart that is used +// to inject additional functionality e.g: Logger, file I/O, socket I/O etc. +#define BUILTIN_NATIVE_LIST(V) \ + V(Logger_PrintString, 1) \ + V(Directory_Close, 2) \ + V(Directory_List, 7) \ + V(Directory_Open, 2) \ + V(File_OpenFile, 3) \ + V(File_Exists, 1) \ + V(File_Close, 1) \ + V(File_ReadByte, 1) \ + V(File_WriteByte, 2) \ + V(File_WriteString, 2) \ + V(File_ReadList, 4) \ + V(File_WriteList , 4) \ + V(File_Position, 1) \ + V(File_Length, 1) \ + V(File_Flush, 1) \ + V(EventHandler_Start, 1) \ + V(EventHandler_SendData, 4) \ + V(Process_Kill, 2) \ + V(Process_Start, 8) \ + V(Socket_CreateConnect, 3) \ + V(Socket_Available, 1) \ + V(Socket_ReadList, 4) \ + V(Socket_WriteList, 4) \ + V(Socket_GetPort, 1) \ + V(ServerSocket_CreateBindListen, 4) \ + V(ServerSocket_Accept, 2) \ + + +BUILTIN_NATIVE_LIST(DECLARE_FUNCTION); + +static struct NativeEntries { + const char* name_; + Dart_NativeFunction function_; + int argument_count_; +} BuiltinEntries[] = { + BUILTIN_NATIVE_LIST(REGISTER_FUNCTION) +}; + + +static Dart_NativeFunction native_lookup(Dart_Handle name, + int argument_count) { + Dart_Result result = Dart_StringToCString(name); + assert(Dart_IsValidResult(result)); + const char* function_name = Dart_GetResultAsCString(result); + assert(function_name != NULL); + int num_entries = sizeof(BuiltinEntries) / sizeof(struct NativeEntries); + for (int i = 0; i < num_entries; i++) { + struct NativeEntries* entry = &(BuiltinEntries[i]); + if (!strcmp(function_name, entry->name_)) { + if (entry->argument_count_ == argument_count) { + return reinterpret_cast(entry->function_); + } else { + // Wrong number of arguments. + // TODO(regis): Should we pass a buffer for error reporting? + return NULL; + } + } + } + return NULL; +} + + +void Builtin_LoadLibrary() { + Dart_Handle url = Dart_NewString(DartUtils::kBuiltinLibURL); + Dart_Result result = Dart_LookupLibrary(url); + if (Dart_IsValidResult(result)) { + // Builtin library already loaded. + return; + } + + // Load the library. + Dart_Handle source = Dart_NewString(Builtin_source_); + result = Dart_LoadLibrary(url, source); + assert(Dart_IsValidResult(result)); + Dart_Handle builtin_lib = Dart_GetResult(result); + + // Lookup the core libraries and inject the builtin library into them. + result = Dart_LookupLibrary(Dart_NewString("dart:core")); + assert(Dart_IsValidResult(result)); + Dart_Handle core_lib = Dart_GetResult(result); + result = Dart_LibraryImportLibrary(core_lib, builtin_lib); + assert(Dart_IsValidResult(result)); + + result = Dart_LookupLibrary(Dart_NewString("dart:coreimpl")); + assert(Dart_IsValidResult(result)); + Dart_Handle coreimpl_lib = Dart_GetResult(result); + result = Dart_LibraryImportLibrary(coreimpl_lib, builtin_lib); + assert(Dart_IsValidResult(result)); + result = Dart_LibraryImportLibrary(builtin_lib, coreimpl_lib); + assert(Dart_IsValidResult(result)); + + // Create a native wrapper "FileNativeWrapper" so that we can add a + // native field to store the OS file handle for implementing all the + // file operations. The class "File" extends "FileNativeWrapper". + Dart_Handle name = Dart_NewString("FileNativeWrapper"); + const int kNumFileFields = 1; + result = Dart_CreateNativeWrapperClass(builtin_lib, name, kNumFileFields); + assert(Dart_IsValidResult(result)); + + // Create a native wrapper "EventHandlerNativeWrapper" so that we can add a + // native field to store the event handle for implementing all + // event operations. + name = Dart_NewString("EventHandlerNativeWrapper"); + const int kNumEventHandlerFields = 1; + result = Dart_CreateNativeWrapperClass(builtin_lib, + name, + kNumEventHandlerFields); + assert(Dart_IsValidResult(result)); +} + + +void Builtin_ImportLibrary(Dart_Handle library) { + Builtin_LoadLibrary(); + + Dart_Handle url = Dart_NewString(DartUtils::kBuiltinLibURL); + Dart_Result result = Dart_LookupLibrary(url); + assert(Dart_IsValidResult(result)); + Dart_Handle builtin_lib = Dart_GetResult(result); + result = Dart_LibraryImportLibrary(library, builtin_lib); + assert(Dart_IsValidResult(result)); +} + + +void Builtin_SetNativeResolver() { + Dart_Handle url = Dart_NewString(DartUtils::kBuiltinLibURL); + Dart_Result result = Dart_LookupLibrary(url); + assert(Dart_IsValidResult(result)); + Dart_Handle builtin_lib = Dart_GetResult(result); + result = Dart_SetNativeResolver(builtin_lib, native_lookup); + assert(Dart_IsValidResult(result)); +} diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc new file mode 100644 index 00000000000..f6101c92428 --- /dev/null +++ b/runtime/bin/dartutils.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "bin/dartutils.h" + +const char* DartUtils::kBuiltinLibURL = "./dart:builtin-lib"; +const char* DartUtils::kBuiltinLibSpec = "library { }"; +const char* DartUtils::kIdFieldName = "_id"; + + +int64_t DartUtils::GetIntegerValue(Dart_Handle value_obj) { + assert(Dart_IsInteger(value_obj)); + Dart_Result result = Dart_IntegerValue(value_obj); + ASSERT(Dart_IsValidResult(result)); + return Dart_GetResultAsCInt64(result); +} + +const char* DartUtils::GetStringValue(Dart_Handle str_obj) { + Dart_Result result = Dart_StringToCString(str_obj); + ASSERT(Dart_IsValidResult(result)); + return Dart_GetResultAsCString(result); +} + + +bool DartUtils::GetBooleanValue(Dart_Handle bool_obj) { + Dart_Result result = Dart_BooleanValue(bool_obj); + ASSERT(Dart_IsValidResult(result)); + return Dart_GetResultAsCBoolean(result); +} + +void DartUtils::SetIntegerInstanceField(Dart_Handle handle, + const char* name, + intptr_t val) { + Dart_Result result = Dart_SetInstanceField(handle, + Dart_NewString(name), + Dart_NewInteger(val)); + ASSERT(Dart_IsValidResult(result)); +} + +intptr_t DartUtils::GetIntegerInstanceField(Dart_Handle handle, + const char* name) { + Dart_Result result = + Dart_GetInstanceField(handle, Dart_NewString(name)); + assert(Dart_IsValidResult(result)); + intptr_t value = DartUtils::GetIntegerValue(Dart_GetResult(result)); + return value; +} + +void DartUtils::SetStringInstanceField(Dart_Handle handle, + const char* name, + const char* val) { + Dart_Result result = Dart_SetInstanceField(handle, + Dart_NewString(name), + Dart_NewString(val)); + ASSERT(Dart_IsValidResult(result)); +} diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h new file mode 100644 index 00000000000..64887227ac1 --- /dev/null +++ b/runtime/bin/dartutils.h @@ -0,0 +1,36 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_DARTUTILS_H_ +#define BIN_DARTUTILS_H_ + +#include "bin/builtin.h" +#include "bin/globals.h" + +#include "include/dart_api.h" + +class DartUtils { + public: + static int64_t GetIntegerValue(Dart_Handle value_obj); + static const char* GetStringValue(Dart_Handle str_obj); + static bool GetBooleanValue(Dart_Handle bool_obj); + static void SetIntegerInstanceField(Dart_Handle handle, + const char* name, + intptr_t val); + static intptr_t GetIntegerInstanceField(Dart_Handle handle, + const char* name); + static void SetStringInstanceField(Dart_Handle handle, + const char* name, + const char* val); + + static const char* kBuiltinLibURL; + static const char* kBuiltinLibSpec; + + static const char* kIdFieldName; + private: + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(DartUtils); +}; + +#endif // BIN_DARTUTILS_H_ diff --git a/runtime/bin/directory.cc b/runtime/bin/directory.cc new file mode 100644 index 00000000000..9fd3d07777c --- /dev/null +++ b/runtime/bin/directory.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "bin/dartutils.h" +#include "bin/directory.h" + +#include "include/dart_api.h" + +void FUNCTION_NAME(Directory_Open)(Dart_NativeArguments args) { + Dart_EnterScope(); + Dart_Handle directory_handle = Dart_GetNativeArgument(args, 0); + Dart_Handle path_handle = Dart_GetNativeArgument(args, 1); + if (!Dart_IsString(path_handle)) { + Dart_SetReturnValue(args, Dart_NewBoolean(false)); + } else { + const char* path = + DartUtils::GetStringValue(path_handle); + intptr_t dir = 0; + bool success = Directory::Open(path, &dir); + if (success) { + DartUtils::SetIntegerInstanceField(directory_handle, + DartUtils::kIdFieldName, + dir); + } + Dart_SetReturnValue(args, Dart_NewBoolean(success)); + } + Dart_ExitScope(); +} + +void FUNCTION_NAME(Directory_Close)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t dir = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1)); + bool success = Directory::Close(dir); + Dart_SetReturnValue(args, Dart_NewBoolean(success)); + Dart_ExitScope(); +} + + +static intptr_t GetHandlerPort(Dart_Handle handle) { + if (Dart_IsNull(handle)) { + // TODO(ager): Generalize this to Directory::kInvalidId. + return 0; + } + return DartUtils::GetIntegerInstanceField(handle, DartUtils::kIdFieldName); +} + + +void FUNCTION_NAME(Directory_List)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t dir = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1)); + bool recursive = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 2)); + Dart_Port dir_handler_port = GetHandlerPort(Dart_GetNativeArgument(args, 3)); + Dart_Port file_handler_port = GetHandlerPort(Dart_GetNativeArgument(args, 4)); + Dart_Port done_handler_port = GetHandlerPort(Dart_GetNativeArgument(args, 5)); + Dart_Port dir_error_handler_port = + GetHandlerPort(Dart_GetNativeArgument(args, 6)); + Directory::List(dir, + recursive, + dir_handler_port, + file_handler_port, + done_handler_port, + dir_error_handler_port); + Dart_ExitScope(); +} diff --git a/runtime/bin/directory.dart b/runtime/bin/directory.dart new file mode 100644 index 00000000000..68ea8703776 --- /dev/null +++ b/runtime/bin/directory.dart @@ -0,0 +1,57 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +interface Directory factory DirectoryImpl { + /** + * Creates a directory object. The path is either a full path or + * relative to the directory in which the Dart VM was + * started. Throws an exception if the path does not specify a + * directory. + */ + Directory.open(String dir); + + /** + * Close this [Directory]. Terminates listing operation if one is in + * progress. Returns a boolean indicating whether the close operation + * succeeded. + */ + bool close(); + + /** + * List the sub-directories and files of this + * [Directory]. Optionally recurse into sub-directories. For each + * file and directory, the file or directory handler is called. When + * all directories have been listed the done handler is called. If + * the listing operation is recursive, the directory error handler + * is called if a subdirectory cannot be opened for listing. + */ + void list([bool recursive]); + + /** + * Sets the directory handler that is called for all directories + * during listing operations. The directory handler is called with + * the full path of the directory. + */ + void setDirHandler(void dirHandler(String dir)); + + /** + * Sets the file handler that is called for all directories during + * listing operations. The directory handler is called with the full + * path of the file. + */ + void setFileHandler(void fileHandler(String file)); + + /** + * Set the done handler that is called either when a directory + * listing is completed or when the close method is called. + */ + void setDoneHandler(void doneHandler(bool completed)); + + /** + * Set the directory error handler that is called for all + * directories that cannot be opened for listing during recursive + * listing. + */ + void setDirErrorHandler(void errorHandler(String dir)); +} diff --git a/runtime/bin/directory.h b/runtime/bin/directory.h new file mode 100644 index 00000000000..6ae47c043a5 --- /dev/null +++ b/runtime/bin/directory.h @@ -0,0 +1,26 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_DIRECTORY_H_ +#define BIN_DIRECTORY_H_ + +#include "bin/builtin.h" +#include "bin/globals.h" + +class Directory { + public: + static bool Open(const char* path, intptr_t* dir); + static bool Close(intptr_t dir); + static void List(intptr_t dir, + bool recursive, + Dart_Port dir_handler, + Dart_Port file_handler, + Dart_Port done_handler, + Dart_Port dir_error_handler); + + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(Directory); +}; + +#endif // BIN_DIRECTORY_H_ diff --git a/runtime/bin/directory_impl.dart b/runtime/bin/directory_impl.dart new file mode 100644 index 00000000000..27815b8bba6 --- /dev/null +++ b/runtime/bin/directory_impl.dart @@ -0,0 +1,130 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class DirectoryException { + const DirectoryException(String this.message); + final String message; +} + + +class DirectoryImpl implements Directory { + + DirectoryImpl.open(String dir) { + _id = 0; + _closed = false; + _listing = false; + if (!_open(dir)) { + _closed = true; + throw new DirectoryException("Error: could not open directory"); + } + } + + bool close() { + if (_closed) { + throw new DirectoryException("Error: directory closed"); + } + if (_close(_id)) { + _closePort(_dirHandler); + _closePort(_fileHandler); + _closePort(_doneHandler); + _closePort(_dirErrorHandler); + _closed = true; + bool was_listing = _listing; + _listing = false; + if (was_listing && _doneHandler !== null) { + _doneHandler(false); + } + return true; + } + return false; + } + + void list([bool recursive = false]) { + if (_closed) { + throw new DirectoryException("Error: directory closed"); + } + if (_listing) { + throw new DirectoryException("Error: listing already in progress"); + } + _listing = true; + _list(_id, + recursive, + _dirHandler, + _fileHandler, + _doneHandler, + _dirErrorHandler); + } + + // TODO(ager): Implement setting of the handlers as in the process library. + void setDirHandler(void dirHandler(String dir)) { + if (_closed) { + throw new DirectoryException("Error: directory closed"); + } + if (_dirHandler === null) { + _dirHandler = new ReceivePort(); + } + _dirHandler.receive((String dir, ignored) => dirHandler(dir)); + } + + void setFileHandler(void fileHandler(String file)) { + if (_closed) { + throw new DirectoryException("Error: directory closed"); + } + if (_fileHandler === null) { + _fileHandler = new ReceivePort(); + } + _fileHandler.receive((String file, ignored) => fileHandler(file)); + } + + void setDoneHandler(void doneHandler(bool completed)) { + if (_closed) { + throw new DirectoryException("Error: directory closed"); + } + if (_doneHandler === null) { + _doneHandler = new ReceivePort(); + } + _doneHandler.receive((bool completed, ignored) { + _listing = false; + doneHandler(completed); + }); + } + + void setDirErrorHandler(void errorHandler(String dir)) { + if (_closed) { + throw new DirectoryException("Error: directory closed"); + } + if (_dirErrorHandler === null) { + _dirErrorHandler = new ReceivePort(); + } + _dirErrorHandler.receive((String dir, ignored) { + errorHandler(dir, completed); + }); + } + + // Utility methods. + void _closePort(ReceivePort port) { + if (port !== null) { + port.close(); + } + } + + // Native code binding. + bool _open(String dir) native "Directory_Open"; + bool _close(int id) native "Directory_Close"; + void _list(int id, + bool recursive, + ReceivePort dirHandler, + ReceivePort fileHandler, + ReceivePort doneHandler, + ReceivePort dirErrorHandler) native "Directory_List"; + + ReceivePort _dirHandler; + ReceivePort _fileHandler; + ReceivePort _doneHandler; + ReceivePort _dirErrorHandler; + + int _id; + bool _closed; + bool _listing; +} diff --git a/runtime/bin/directory_linux.cc b/runtime/bin/directory_linux.cc new file mode 100644 index 00000000000..77f6d88780b --- /dev/null +++ b/runtime/bin/directory_linux.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include + +#include "bin/directory.h" + +bool Directory::Open(const char* path, intptr_t* dir) { + DIR* dir_pointer = opendir(path); + if (dir_pointer == NULL) { + return false; + } + *dir = reinterpret_cast(dir_pointer); + return true; +} + +bool Directory::Close(intptr_t dir) { + DIR* dir_pointer = reinterpret_cast(dir); + int result = closedir(dir_pointer); + return result == 0; +} + +void Directory::List(intptr_t dir, + bool recursive, + Dart_Port dir_handler, + Dart_Port file_handler, + Dart_Port done_handler, + Dart_Port dir_error_handler) { + // TODO(ager): Handle recursion and errors caused by recursion. + // TODO(ager): Make this async by using a thread to do this. + DIR* dir_pointer = reinterpret_cast(dir); + int success = 0; + dirent entry; + dirent* result; + while ((success = readdir_r(dir_pointer, &entry, &result)) == 0 && + result != NULL) { + switch (entry.d_type) { + case DT_DIR: + if (dir_handler != 0 && + strcmp(entry.d_name, ".") != 0 && + strcmp(entry.d_name, "..") != 0) { + Dart_Handle name = Dart_NewString(entry.d_name); + Dart_Post(dir_handler, name); + } + break; + case DT_REG: + if (file_handler != 0) { + Dart_Handle name = Dart_NewString(entry.d_name); + Dart_Post(file_handler, name); + } + break; + case DT_UNKNOWN: + UNIMPLEMENTED(); + // TODO(ager): Handle this correctly. Use lstat or something? + break; + default: + // TODO(ager): Handle symbolic links? + break; + } + } + if (success != 0) { + // TODO(ager): Error listing directory. There should probably be + // a general error handler. Maybe collaps the dir_error_handler + // to just be a general error handler. + } else if (done_handler != 0) { + Dart_Handle value = Dart_NewBoolean(true); + Dart_Post(done_handler, value); + } +} diff --git a/runtime/bin/directory_macos.cc b/runtime/bin/directory_macos.cc new file mode 100644 index 00000000000..77f6d88780b --- /dev/null +++ b/runtime/bin/directory_macos.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include + +#include "bin/directory.h" + +bool Directory::Open(const char* path, intptr_t* dir) { + DIR* dir_pointer = opendir(path); + if (dir_pointer == NULL) { + return false; + } + *dir = reinterpret_cast(dir_pointer); + return true; +} + +bool Directory::Close(intptr_t dir) { + DIR* dir_pointer = reinterpret_cast(dir); + int result = closedir(dir_pointer); + return result == 0; +} + +void Directory::List(intptr_t dir, + bool recursive, + Dart_Port dir_handler, + Dart_Port file_handler, + Dart_Port done_handler, + Dart_Port dir_error_handler) { + // TODO(ager): Handle recursion and errors caused by recursion. + // TODO(ager): Make this async by using a thread to do this. + DIR* dir_pointer = reinterpret_cast(dir); + int success = 0; + dirent entry; + dirent* result; + while ((success = readdir_r(dir_pointer, &entry, &result)) == 0 && + result != NULL) { + switch (entry.d_type) { + case DT_DIR: + if (dir_handler != 0 && + strcmp(entry.d_name, ".") != 0 && + strcmp(entry.d_name, "..") != 0) { + Dart_Handle name = Dart_NewString(entry.d_name); + Dart_Post(dir_handler, name); + } + break; + case DT_REG: + if (file_handler != 0) { + Dart_Handle name = Dart_NewString(entry.d_name); + Dart_Post(file_handler, name); + } + break; + case DT_UNKNOWN: + UNIMPLEMENTED(); + // TODO(ager): Handle this correctly. Use lstat or something? + break; + default: + // TODO(ager): Handle symbolic links? + break; + } + } + if (success != 0) { + // TODO(ager): Error listing directory. There should probably be + // a general error handler. Maybe collaps the dir_error_handler + // to just be a general error handler. + } else if (done_handler != 0) { + Dart_Handle value = Dart_NewBoolean(true); + Dart_Post(done_handler, value); + } +} diff --git a/runtime/bin/directory_win.cc b/runtime/bin/directory_win.cc new file mode 100644 index 00000000000..cafce6887ea --- /dev/null +++ b/runtime/bin/directory_win.cc @@ -0,0 +1,24 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "bin/directory.h" + +bool Directory::Open(const char* path, intptr_t* dir) { + UNIMPLEMENTED(); + return false; +} + +bool Directory::Close(intptr_t dir) { + UNIMPLEMENTED(); + return false; +} + +void Directory::List(intptr_t dir, + bool recursive, + Dart_Port dir_handler, + Dart_Port file_handler, + Dart_Port done_handler, + Dart_Port dir_error_handler) { + UNIMPLEMENTED(); +} diff --git a/runtime/bin/eventhandler.cc b/runtime/bin/eventhandler.cc new file mode 100644 index 00000000000..8534def6a38 --- /dev/null +++ b/runtime/bin/eventhandler.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "bin/eventhandler.h" +#include "bin/dartutils.h" + +#include "include/dart_api.h" + + +static const intptr_t kNativeEventHandlerFieldIndex = 0; + + +/* + * Returns the reference of the EventHandler stored in the native field. + */ +static EventHandler* GetEventHandler(Dart_Handle handle) { + Dart_Result result = Dart_GetNativeInstanceField( + handle, kNativeEventHandlerFieldIndex); + assert(Dart_IsValidResult(result)); + EventHandler* event_handler = + reinterpret_cast(Dart_GetResultAsCIntptr(result)); + assert(event_handler != NULL); + return event_handler; +} + + +/* + * Sets the reference of the EventHandler in the native field. + */ +static void SetEventHandler(Dart_Handle handle, EventHandler* event_handler) { + Dart_SetNativeInstanceField(handle, + kNativeEventHandlerFieldIndex, + reinterpret_cast(event_handler)); +} + + +/* + * Starts the EventHandler thread and stores its reference in the dart + * EventHandler object. args[0] holds the reference to the dart EventHandler + * object. + */ +void FUNCTION_NAME(EventHandler_Start)(Dart_NativeArguments args) { + Dart_EnterScope(); + Dart_Handle handle = Dart_GetNativeArgument(args, 0); + EventHandler* event_handler = EventHandler::StartEventHandler(); + SetEventHandler(handle, event_handler); + Dart_ExitScope(); +} + + +/* + * Send data to the EventHandler thread to register for a given instance + * args[1] a ReceivePort args[2] with a notification event args[3]. args[0] + * holds the reference to the dart EventHandler object. + */ +void FUNCTION_NAME(EventHandler_SendData)(Dart_NativeArguments args) { + Dart_EnterScope(); + Dart_Handle handle = Dart_GetNativeArgument(args, 0); + EventHandler* event_handler = GetEventHandler(handle); + intptr_t id = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1)); + handle = Dart_GetNativeArgument(args, 2); + Dart_Port dart_port = + DartUtils::GetIntegerInstanceField(handle, DartUtils::kIdFieldName); + intptr_t data = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 3)); + event_handler->SendData(id, dart_port, data); + Dart_ExitScope(); +} diff --git a/runtime/bin/eventhandler.dart b/runtime/bin/eventhandler.dart new file mode 100644 index 00000000000..7c558a90ee9 --- /dev/null +++ b/runtime/bin/eventhandler.dart @@ -0,0 +1,28 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class EventHandler extends EventHandlerNativeWrapper { + EventHandler() { } + + static void _start() { + if (_eventHandler === null) { + _eventHandler = new EventHandler(); + _eventHandler._doStart(); + } + } + + void _doStart() native "EventHandler_Start"; + + static _sendData(int id, ReceivePort receivePort, int data) { + if (_eventHandler !== null) { + _eventHandler._doSendData(id, receivePort, data); + } + } + + + void _doSendData(int id, ReceivePort receivePort, int data) + native "EventHandler_SendData"; + + static EventHandler _eventHandler; +} diff --git a/runtime/bin/eventhandler.h b/runtime/bin/eventhandler.h new file mode 100644 index 00000000000..381ad8ff673 --- /dev/null +++ b/runtime/bin/eventhandler.h @@ -0,0 +1,50 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_EVENTHANDLER_H_ +#define BIN_EVENTHANDLER_H_ + +#include "bin/builtin.h" + +// The event handler delegation class is OS specific. +#if defined(TARGET_OS_LINUX) +#include "bin/eventhandler_linux.h" +#elif defined(TARGET_OS_MACOS) +#include "bin/eventhandler_macos.h" +#elif defined(TARGET_OS_WINDOWS) +#include "bin/eventhandler_win.h" +#else +#error Unknown target os. +#endif + +/* + * Keep these constant in sync with the dart poll event identifiers. + */ +enum Message { + kInEvent = 0, + kOutEvent, + kErrorEvent, + kCloseEvent, + kCloseCommand, +}; + + +class EventHandler { + public: + void SendData(intptr_t id, Dart_Port dart_port, intptr_t data) { + delegate_.SendData(id, dart_port, data); + } + + static EventHandler* StartEventHandler() { + EventHandler* handler = new EventHandler(); + handler->delegate_.StartEventHandler(); + return handler; + } + + private: + EventHandlerImplementation delegate_; +}; + + +#endif // BIN_EVENTHANDLER_H_ diff --git a/runtime/bin/eventhandler_linux.cc b/runtime/bin/eventhandler_linux.cc new file mode 100644 index 00000000000..169ddbc6b3a --- /dev/null +++ b/runtime/bin/eventhandler_linux.cc @@ -0,0 +1,327 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include + +#include "bin/eventhandler.h" +#include "bin/fdutils.h" + + +int64_t GetCurrentTimeMilliseconds() { + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) { + UNREACHABLE(); + return 0; + } + return ((static_cast(tv.tv_sec) * 1000000) + tv.tv_usec) / 1000; +} + + +static const int kInitialPortMapSize = 128; +static const int kPortMapGrowingFactor = 2; +static const int kInterruptMessageSize = sizeof(InterruptMessage); +static const int kInfinityTimeout = -1; +static const int kTimerId = -1; + + +EventHandlerImplementation::EventHandlerImplementation() { + intptr_t result; + port_map_entries_ = 0; + port_map_size_ = kInitialPortMapSize; + port_map_ = reinterpret_cast(calloc(port_map_size_, + sizeof(PortData))); + ASSERT(port_map_ != NULL); + result = pipe(interrupt_fds_); + if (result != 0) { + FATAL("Pipe creation failed"); + } + FDUtils::SetNonBlocking(interrupt_fds_[0]); + FDUtils::SetNonBlocking(interrupt_fds_[1]); + timeout_ = kInfinityTimeout; + timeout_port_ = 0; +} + + +EventHandlerImplementation::~EventHandlerImplementation() { + free(port_map_); + close(interrupt_fds_[0]); + close(interrupt_fds_[1]); +} + + +// TODO(hpayer): Use hash table instead of array. +void EventHandlerImplementation::SetPort(intptr_t fd, + Dart_Port dart_port, + intptr_t mask) { + assert(fd >= 0); + if (fd >= port_map_size_) { + intptr_t new_port_map_size = port_map_size_; + do { + new_port_map_size = new_port_map_size * kPortMapGrowingFactor; + } while (fd >= new_port_map_size); + size_t new_port_map_bytes = new_port_map_size * sizeof(PortData); + port_map_ = reinterpret_cast(realloc(port_map_, + new_port_map_bytes)); + ASSERT(port_map_ != NULL); + size_t port_map_bytes = port_map_size_ * sizeof(PortData); + memset(port_map_ + port_map_bytes, + 0, + new_port_map_bytes - port_map_bytes); + port_map_size_ = new_port_map_size; + } + + /* + * Only change the port map entries count if SetPort changes + * the port map state. + */ + if (dart_port == 0 && PortFor(fd) != 0) { + port_map_entries_--; + } else if (dart_port != 0 && PortFor(fd) == 0) { + port_map_entries_++; + } + port_map_[fd].dart_port = dart_port; + port_map_[fd].mask = mask; +} + + +Dart_Port EventHandlerImplementation::PortFor(intptr_t fd) { + return port_map_[fd].dart_port; +} + + +void EventHandlerImplementation::RegisterFdWakeup(intptr_t id, + Dart_Port dart_port, + intptr_t data) { + WakeupHandler(id, dart_port, data); +} + + +void EventHandlerImplementation::CloseFd(intptr_t id) { + SetPort(id, 0, 0); + close(id); +} + + +void EventHandlerImplementation::UnregisterFdWakeup(intptr_t id) { + WakeupHandler(id, 0, 0); +} + + +void EventHandlerImplementation::UnregisterFd(intptr_t id) { + SetPort(id, 0, 0); +} + + +void EventHandlerImplementation::WakeupHandler(intptr_t id, + Dart_Port dart_port, + int64_t data) { + InterruptMessage msg; + msg.id = id; + msg.dart_port = dart_port; + msg.data = data; + intptr_t result = + write(interrupt_fds_[1], &msg, kInterruptMessageSize); + if (result != kInterruptMessageSize) { + perror("Interrupt message failure"); + } +} + + +void EventHandlerImplementation::SetPollEvents(struct pollfd* pollfds, + intptr_t mask) { + /* + * We do not set POLLERR and POLLHUP explicitly since they are triggered + * anyway. + */ + pollfds->events |= POLLRDHUP; + if ((mask & (1 << kInEvent)) != 0) { + pollfds->events |= POLLIN; + } + if ((mask & (1 << kOutEvent)) != 0) { + pollfds->events |= POLLOUT; + } +} + + +struct pollfd* EventHandlerImplementation::GetPollFds(intptr_t* pollfds_size) { + struct pollfd* pollfds; + + intptr_t numPollfds = 1 + port_map_entries_; + pollfds = reinterpret_cast(calloc(sizeof(struct pollfd), + numPollfds)); + pollfds[0].fd = interrupt_fds_[0]; + pollfds[0].events |= POLLIN; + + // TODO(hpayer): optimize the following iteration over the hash map + int j = 1; + for (int i = 0; i < port_map_size_; i++) { + if (port_map_[i].dart_port != 0) { + // Fd is added to the poll set. + pollfds[j].fd = i; + SetPollEvents(&pollfds[j], port_map_[i].mask); + j++; + } + } + *pollfds_size = numPollfds; + return pollfds; +} + + +bool EventHandlerImplementation::GetInterruptMessage(InterruptMessage* msg) { + int total_read = 0; + int bytes_read = read(interrupt_fds_[0], msg, kInterruptMessageSize); + if (bytes_read < 0) { + return false; + } + total_read = bytes_read; + while (total_read < kInterruptMessageSize) { + bytes_read = read(interrupt_fds_[0], + msg + total_read, + kInterruptMessageSize - total_read); + if (bytes_read > 0) { + total_read = total_read + bytes_read; + } + } + return (total_read == kInterruptMessageSize) ? true : false; +} + +void EventHandlerImplementation::HandleInterruptFd() { + InterruptMessage msg; + while (GetInterruptMessage(&msg)) { + if (msg.id == kTimerId) { + timeout_ = msg.data; + timeout_port_ = msg.dart_port; + } else if ((msg.data & (1 << kCloseCommand)) != 0) { + /* + * A close event happened in dart, we have to explicitly unregister + * the fd and close the fd. + */ + CloseFd(msg.id); + } else { + SetPort(msg.id, msg.dart_port, msg.data); + } + } +} + + +intptr_t EventHandlerImplementation::GetPollEvents(struct pollfd* pollfd) { + intptr_t event_mask = 0; + /* + * We prioritize the events in the following order. + */ + if ((pollfd->revents & POLLIN) != 0) { + if (FDUtils::AvailableBytes(pollfd->fd) != 0) { + event_mask = (1 << kInEvent); + } else if (((pollfd->revents & POLLHUP) != 0) || + ((pollfd->revents & POLLRDHUP) != 0)) { + event_mask = (1 << kCloseEvent); + } else if ((pollfd->revents & POLLERR) != 0) { + event_mask = (1 << kErrorEvent); + } else { + /* + * Accept event. + */ + event_mask = (1 << kInEvent); + } + } + + if ((pollfd->revents & POLLOUT) != 0) { + event_mask |= (1 << kOutEvent); + } + + return event_mask; +} + + +void EventHandlerImplementation::HandleEvents(struct pollfd* pollfds, + int pollfds_size, + int result_size) { + if ((pollfds[0].revents & POLLIN) != 0) { + result_size -= 1; + } + if (result_size > 0) { + for (int i = 1; i < pollfds_size; i++) { + /* + * The fd is unregistered. It gets re-registered when the request + * was handled by dart. + */ + intptr_t event_mask = GetPollEvents(&pollfds[i]); + if (event_mask != 0) { + intptr_t fd = pollfds[i].fd; + Dart_Port port = PortFor(fd); + assert(port != 0); + UnregisterFd(fd); + Dart_PostIntArray(port, 1, &event_mask); + } + } + } + HandleInterruptFd(); +} + + +intptr_t EventHandlerImplementation::GetTimeout() { + if (timeout_ == kInfinityTimeout) { + return kInfinityTimeout; + } + intptr_t millis = timeout_ - GetCurrentTimeMilliseconds(); + return (millis < 0) ? 0 : millis; +} + + +void EventHandlerImplementation::HandleTimeout() { + if (timeout_ != kInfinityTimeout) { + intptr_t millis = timeout_ - GetCurrentTimeMilliseconds(); + if (millis <= 0) { + Dart_PostIntArray(timeout_port_, 0, NULL); + timeout_ = kInfinityTimeout; + timeout_port_ = 0; + } + } +} + + +void* EventHandlerImplementation::Poll(void* args) { + intptr_t pollfds_size; + struct pollfd* pollfds; + EventHandlerImplementation* handler = + reinterpret_cast(args); + while (1) { + pollfds = handler->GetPollFds(&pollfds_size); + intptr_t millis = handler->GetTimeout(); + intptr_t result = poll(pollfds, pollfds_size, millis); + if (result == -1) { + perror("Poll failed"); + } else { + handler->HandleTimeout(); + handler->HandleEvents(pollfds, pollfds_size, result); + } + free(pollfds); + } + return NULL; +} + + +void EventHandlerImplementation::StartEventHandler() { + pthread_t handler_thread; + int result = pthread_create(&handler_thread, + NULL, + &EventHandlerImplementation::Poll, + this); + if (result != 0) { + FATAL("Create start event handler thread"); + } +} + + +void EventHandlerImplementation::SendData(intptr_t id, + Dart_Port dart_port, + intptr_t data) { + RegisterFdWakeup(id, dart_port, data); +} diff --git a/runtime/bin/eventhandler_linux.h b/runtime/bin/eventhandler_linux.h new file mode 100644 index 00000000000..95a1fa487ae --- /dev/null +++ b/runtime/bin/eventhandler_linux.h @@ -0,0 +1,57 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_EVENTHANDLER_LINUX_H_ +#define BIN_EVENTHANDLER_LINUX_H_ + + +typedef struct { + intptr_t id; + Dart_Port dart_port; + int64_t data; +} InterruptMessage; + + +typedef struct { + Dart_Port dart_port; + intptr_t mask; +} PortData; + + +class EventHandlerImplementation { + public: + EventHandlerImplementation(); + ~EventHandlerImplementation(); + + void SendData(intptr_t id, Dart_Port dart_port, intptr_t data); + void StartEventHandler(); + + private: + intptr_t GetTimeout(); + bool GetInterruptMessage(InterruptMessage* msg); + struct pollfd* GetPollFds(intptr_t* size); + void RegisterFdWakeup(intptr_t id, Dart_Port dart_port, intptr_t data); + void UnregisterFdWakeup(intptr_t id); + void CloseFd(intptr_t id); + void UnregisterFd(intptr_t id); + void HandleEvents(struct pollfd* pollfds, int pollfds_size, int result_size); + void HandleTimeout(); + static void* Poll(void* args); + void WakeupHandler(intptr_t id, Dart_Port dart_port, int64_t data); + void HandleInterruptFd(); + void SetPort(intptr_t fd, Dart_Port dart_port, intptr_t mask); + Dart_Port PortFor(intptr_t fd); + intptr_t GetPollEvents(struct pollfd* pollfd); + void SetPollEvents(struct pollfd* pollfds, intptr_t mask); + + PortData* port_map_; + intptr_t port_map_entries_; + intptr_t port_map_size_; + int64_t timeout_; // Time for next timeout. + Dart_Port timeout_port_; + int interrupt_fds_[2]; +}; + + +#endif // BIN_EVENTHANDLER_LINUX_H_ diff --git a/runtime/bin/eventhandler_macos.cc b/runtime/bin/eventhandler_macos.cc new file mode 100644 index 00000000000..45ea418cf46 --- /dev/null +++ b/runtime/bin/eventhandler_macos.cc @@ -0,0 +1,325 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include + +#include "bin/eventhandler.h" +#include "bin/fdutils.h" + + +int64_t GetCurrentTimeMilliseconds() { + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) { + UNREACHABLE(); + return 0; + } + return ((static_cast(tv.tv_sec) * 1000000) + tv.tv_usec) / 1000; +} + + +static const int kInitialPortMapSize = 128; +static const int kPortMapGrowingFactor = 2; +static const int kInterruptMessageSize = sizeof(InterruptMessage); +static const int kInfinityTimeout = -1; +static const int kTimerId = -1; + + +EventHandlerImplementation::EventHandlerImplementation() { + intptr_t result; + port_map_entries_ = 0; + port_map_size_ = kInitialPortMapSize; + port_map_ = reinterpret_cast(calloc(port_map_size_, + sizeof(PortData))); + ASSERT(port_map_ != NULL); + result = pipe(interrupt_fds_); + if (result != 0) { + FATAL("Pipe creation failed"); + } + FDUtils::SetNonBlocking(interrupt_fds_[0]); + FDUtils::SetNonBlocking(interrupt_fds_[1]); + timeout_ = kInfinityTimeout; + timeout_port_ = 0; +} + + +EventHandlerImplementation::~EventHandlerImplementation() { + free(port_map_); + close(interrupt_fds_[0]); + close(interrupt_fds_[1]); +} + + +// TODO(hpayer): Use hash table instead of array. +void EventHandlerImplementation::SetPort(intptr_t fd, + Dart_Port dart_port, + intptr_t mask) { + assert(fd >= 0); + if (fd >= port_map_size_) { + intptr_t new_port_map_size = port_map_size_; + do { + new_port_map_size = new_port_map_size * kPortMapGrowingFactor; + } while (fd >= new_port_map_size); + size_t new_port_map_bytes = new_port_map_size * sizeof(PortData); + port_map_ = reinterpret_cast(realloc(port_map_, + new_port_map_bytes)); + ASSERT(port_map_ != NULL); + size_t port_map_bytes = port_map_size_ * sizeof(PortData); + memset(port_map_ + port_map_bytes, + 0, + new_port_map_bytes - port_map_bytes); + port_map_size_ = new_port_map_size; + } + + /* + * Only change the port map entries count if SetPort changes + * the port map state. + */ + if (dart_port == 0 && PortFor(fd) != 0) { + port_map_entries_--; + } else if (dart_port != 0 && PortFor(fd) == 0) { + port_map_entries_++; + } + port_map_[fd].dart_port = dart_port; + port_map_[fd].mask = mask; +} + + +Dart_Port EventHandlerImplementation::PortFor(intptr_t fd) { + return port_map_[fd].dart_port; +} + + +void EventHandlerImplementation::RegisterFdWakeup(intptr_t id, + Dart_Port dart_port, + intptr_t data) { + WakeupHandler(id, dart_port, data); +} + + +void EventHandlerImplementation::CloseFd(intptr_t id) { + SetPort(id, 0, 0); + close(id); +} + + +void EventHandlerImplementation::UnregisterFdWakeup(intptr_t id) { + WakeupHandler(id, 0, 0); +} + + +void EventHandlerImplementation::UnregisterFd(intptr_t id) { + SetPort(id, 0, 0); +} + + +void EventHandlerImplementation::WakeupHandler(intptr_t id, + Dart_Port dart_port, + int64_t data) { + InterruptMessage msg; + msg.id = id; + msg.dart_port = dart_port; + msg.data = data; + intptr_t result = + write(interrupt_fds_[1], &msg, kInterruptMessageSize); + if (result != kInterruptMessageSize) { + perror("Interrupt message failure"); + } +} + + +void EventHandlerImplementation::SetPollEvents(struct pollfd* pollfds, + intptr_t mask) { + /* + * We do not set POLLERR and POLLHUP explicitly since they are triggered + * anyway. + */ + if ((mask & (1 << kInEvent)) != 0) { + pollfds->events |= POLLIN; + } + if ((mask & (1 << kOutEvent)) != 0) { + pollfds->events |= POLLOUT; + } +} + + +struct pollfd* EventHandlerImplementation::GetPollFds(intptr_t* pollfds_size) { + struct pollfd* pollfds; + + intptr_t numPollfds = 1 + port_map_entries_; + pollfds = reinterpret_cast(calloc(sizeof(struct pollfd), + numPollfds)); + pollfds[0].fd = interrupt_fds_[0]; + pollfds[0].events |= POLLIN; + + // TODO(hpayer): optimize the following iteration over the hash map + int j = 1; + for (int i = 0; i < port_map_size_; i++) { + if (port_map_[i].dart_port != 0) { + // Fd is added to the poll set. + pollfds[j].fd = i; + SetPollEvents(&pollfds[j], port_map_[i].mask); + j++; + } + } + *pollfds_size = numPollfds; + return pollfds; +} + + +bool EventHandlerImplementation::GetInterruptMessage(InterruptMessage* msg) { + int total_read = 0; + int bytes_read = read(interrupt_fds_[0], msg, kInterruptMessageSize); + if (bytes_read < 0) { + return false; + } + total_read = bytes_read; + while (total_read < kInterruptMessageSize) { + bytes_read = read(interrupt_fds_[0], + msg + total_read, + kInterruptMessageSize - total_read); + if (bytes_read > 0) { + total_read = total_read + bytes_read; + } + } + return (total_read == kInterruptMessageSize) ? true : false; +} + +void EventHandlerImplementation::HandleInterruptFd() { + InterruptMessage msg; + while (GetInterruptMessage(&msg)) { + if (msg.id == kTimerId) { + timeout_ = msg.data; + timeout_port_ = msg.dart_port; + } else if ((msg.data & (1 << kCloseCommand)) != 0) { + /* + * A close event happened in dart, we have to explicitly unregister + * the fd and close the fd. + */ + CloseFd(msg.id); + } else { + SetPort(msg.id, msg.dart_port, msg.data); + } + } +} + + +intptr_t EventHandlerImplementation::GetPollEvents(struct pollfd* pollfd) { + intptr_t event_mask = 0; + /* + * We prioritize the events in the following order. + */ + if ((pollfd->revents & POLLIN) != 0) { + if (FDUtils::AvailableBytes(pollfd->fd) != 0) { + event_mask = (1 << kInEvent); + } else if ((pollfd->revents & POLLHUP) != 0) { + event_mask = (1 << kCloseEvent); + } else if ((pollfd->revents & POLLERR) != 0) { + event_mask = (1 << kErrorEvent); + } else { + /* + * Accept event. + */ + event_mask = (1 << kInEvent); + } + } + + if ((pollfd->revents & POLLOUT) != 0) { + event_mask |= (1 << kOutEvent); + } + + return event_mask; +} + + +void EventHandlerImplementation::HandleEvents(struct pollfd* pollfds, + int pollfds_size, + int result_size) { + if ((pollfds[0].revents & POLLIN) != 0) { + result_size -= 1; + } + if (result_size > 0) { + for (int i = 1; i < pollfds_size; i++) { + /* + * The fd is unregistered. It gets re-registered when the request + * was handled by dart. + */ + intptr_t event_mask = GetPollEvents(&pollfds[i]); + if (event_mask != 0) { + intptr_t fd = pollfds[i].fd; + Dart_Port port = PortFor(fd); + assert(port != 0); + UnregisterFd(fd); + Dart_PostIntArray(port, 1, &event_mask); + } + } + } + HandleInterruptFd(); +} + + +intptr_t EventHandlerImplementation::GetTimeout() { + if (timeout_ == kInfinityTimeout) { + return kInfinityTimeout; + } + intptr_t millis = timeout_ - GetCurrentTimeMilliseconds(); + return (millis < 0) ? 0 : millis; +} + + +void EventHandlerImplementation::HandleTimeout() { + if (timeout_ != kInfinityTimeout) { + intptr_t millis = timeout_ - GetCurrentTimeMilliseconds(); + if (millis <= 0) { + Dart_PostIntArray(timeout_port_, 0, NULL); + timeout_ = kInfinityTimeout; + timeout_port_ = 0; + } + } +} + + +void* EventHandlerImplementation::Poll(void* args) { + intptr_t pollfds_size; + struct pollfd* pollfds; + EventHandlerImplementation* handler = + reinterpret_cast(args); + while (1) { + pollfds = handler->GetPollFds(&pollfds_size); + intptr_t millis = handler->GetTimeout(); + intptr_t result = poll(pollfds, pollfds_size, millis); + if (result == -1) { + perror("Poll failed"); + } else { + handler->HandleTimeout(); + handler->HandleEvents(pollfds, pollfds_size, result); + } + free(pollfds); + } + return NULL; +} + + +void EventHandlerImplementation::StartEventHandler() { + pthread_t handler_thread; + int result = pthread_create(&handler_thread, + NULL, + &EventHandlerImplementation::Poll, + this); + if (result != 0) { + FATAL("Create start event handler thread"); + } +} + + +void EventHandlerImplementation::SendData(intptr_t id, + Dart_Port dart_port, + intptr_t data) { + RegisterFdWakeup(id, dart_port, data); +} diff --git a/runtime/bin/eventhandler_macos.h b/runtime/bin/eventhandler_macos.h new file mode 100644 index 00000000000..f67f15ce57a --- /dev/null +++ b/runtime/bin/eventhandler_macos.h @@ -0,0 +1,57 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_EVENTHANDLER_MACOS_H_ +#define BIN_EVENTHANDLER_MACOS_H_ + + +typedef struct { + intptr_t id; + Dart_Port dart_port; + int64_t data; +} InterruptMessage; + + +typedef struct { + Dart_Port dart_port; + intptr_t mask; +} PortData; + + +class EventHandlerImplementation { + public: + EventHandlerImplementation(); + ~EventHandlerImplementation(); + + void SendData(intptr_t id, Dart_Port dart_port, intptr_t data); + void StartEventHandler(); + + private: + intptr_t GetTimeout(); + bool GetInterruptMessage(InterruptMessage* msg); + struct pollfd* GetPollFds(intptr_t* size); + void RegisterFdWakeup(intptr_t id, Dart_Port dart_port, intptr_t data); + void UnregisterFdWakeup(intptr_t id); + void CloseFd(intptr_t id); + void UnregisterFd(intptr_t id); + void HandleEvents(struct pollfd* pollfds, int pollfds_size, int result_size); + void HandleTimeout(); + static void* Poll(void* args); + void WakeupHandler(intptr_t id, Dart_Port dart_port, int64_t data); + void HandleInterruptFd(); + void SetPort(intptr_t fd, Dart_Port dart_port, intptr_t mask); + Dart_Port PortFor(intptr_t fd); + intptr_t GetPollEvents(struct pollfd* pollfd); + void SetPollEvents(struct pollfd* pollfds, intptr_t mask); + + PortData* port_map_; + intptr_t port_map_entries_; + intptr_t port_map_size_; + int64_t timeout_; // Time for next timeout. + Dart_Port timeout_port_; + int interrupt_fds_[2]; +}; + + +#endif // BIN_EVENTHANDLER_MACOS_H_ diff --git a/runtime/bin/eventhandler_win.cc b/runtime/bin/eventhandler_win.cc new file mode 100644 index 00000000000..8e9a0593c55 --- /dev/null +++ b/runtime/bin/eventhandler_win.cc @@ -0,0 +1,778 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include + +#include "bin/builtin.h" +#include "bin/eventhandler.h" +#include "bin/socket.h" + + +static const int kInfinityTimeout = -1; + + +int64_t GetCurrentTimeMilliseconds() { + static const int64_t kTimeEpoc = 116444736000000000LL; + + // Although win32 uses 64-bit integers for representing timestamps, + // these are packed into a FILETIME structure. The FILETIME structure + // is just a struct representing a 64-bit integer. The TimeStamp union + // allows access to both a FILETIME and an integer representation of + // the timestamp. + union TimeStamp { + FILETIME ft_; + int64_t t_; + }; + TimeStamp time; + GetSystemTimeAsFileTime(&time.ft_); + return (time.t_ - kTimeEpoc) / 10000; +} + +IOBuffer* IOBuffer::AllocateBuffer(int buffer_size, Operation operation) { + IOBuffer* buffer = new(buffer_size) IOBuffer(buffer_size, operation); + return buffer; +} + + +IOBuffer* IOBuffer::AllocateAcceptBuffer(int buffer_size) { + IOBuffer* buffer = AllocateBuffer(buffer_size, kAccept); + return buffer; +} + + +IOBuffer* IOBuffer::AllocateReadBuffer(int buffer_size) { + return AllocateBuffer(buffer_size, kRead); +} + + +IOBuffer* IOBuffer::AllocateWriteBuffer(int buffer_size) { + return AllocateBuffer(buffer_size, kWrite); +} + + +void IOBuffer::DisposeBuffer(IOBuffer* buffer) { + delete buffer; +} + + +IOBuffer* IOBuffer::GetFromOverlapped(OVERLAPPED* overlapped) { + IOBuffer* buffer = CONTAINING_RECORD(overlapped, IOBuffer, overlapped_); + return buffer; +} + + +int IOBuffer::Read(void* buffer, int num_bytes) { + if (num_bytes > GetRemainingLength()) { + num_bytes = GetRemainingLength(); + } + memcpy(buffer, GetBufferStart() + index_, num_bytes); + index_ += num_bytes; + return num_bytes; +} + + +int IOBuffer::Write(const void* buffer, int num_bytes) { + ASSERT(num_bytes == buflen_); + memcpy(GetBufferStart(), buffer, num_bytes); + data_length_ = num_bytes; + return num_bytes; +} + + +int IOBuffer::GetRemainingLength() { + ASSERT(operation_ == kRead); + return data_length_ - index_; +} + + +Handle::Handle(HANDLE handle) + : handle_(reinterpret_cast(handle)), + closing_(false), + port_(0), + completion_port_(INVALID_HANDLE_VALUE), + event_handler_(NULL), + data_ready_(NULL), + pending_read_(NULL), + pending_write_(NULL) { + InitializeCriticalSection(&cs_); +} + + +Handle::Handle(HANDLE handle, Dart_Port port) + : handle_(reinterpret_cast(handle)), + closing_(false), + port_(port), + completion_port_(INVALID_HANDLE_VALUE), + event_handler_(NULL), + data_ready_(NULL), + pending_read_(NULL), + pending_write_(NULL) { + InitializeCriticalSection(&cs_); +} + + +Handle::~Handle() { + DeleteCriticalSection(&cs_); +} + + +void Handle::Lock() { + EnterCriticalSection(&cs_); +} + + +void Handle::Unlock() { + LeaveCriticalSection(&cs_); +} + + +bool Handle::CreateCompletionPort(HANDLE completion_port) { + completion_port_ = CreateIoCompletionPort(handle_, + completion_port, + reinterpret_cast(this), + 0); + if (completion_port_ == NULL) { + fprintf(stderr, "Error CreateIoCompletionPort: %d\n", GetLastError()); + return false; + } + return true; +} + + +void Handle::close() { + ScopedLock lock(this); + if (!closing_) { + // Close the socket and set the closing state. This close method can be + // called again if this socket has pending IO operations in flight. + ASSERT(handle_ != INVALID_HANDLE_VALUE); + closing_ = true; + // According to the documentation from Microsoft socket handles should + // not be closed using CloseHandle but using closesocket. + if (is_socket()) { + closesocket(reinterpret_cast(handle_)); + } else { + CloseHandle(handle_); + } + handle_ = INVALID_HANDLE_VALUE; + } + + // Perform socket type specific close handling. + AfterClose(); +} + + +bool Handle::HasPendingRead() { + ScopedLock lock(this); + return pending_read_ != NULL; +} + + +bool Handle::HasPendingWrite() { + ScopedLock lock(this); + return pending_write_ != NULL; +} + + +void Handle::ReadComplete(IOBuffer* buffer) { + ScopedLock lock(this); + // Currently only one outstanding read at the time. + ASSERT(pending_read_ == buffer); + ASSERT(data_ready_ == NULL); + if (!closing_ && !buffer->IsEmpty()) { + data_ready_ = pending_read_; + } else { + IOBuffer::DisposeBuffer(buffer); + } + pending_read_ = NULL; +} + + +void Handle::WriteComplete(IOBuffer* buffer) { + ScopedLock lock(this); + // Currently only one outstanding write at the time. + ASSERT(pending_write_ == buffer); + IOBuffer::DisposeBuffer(buffer); + pending_write_ = NULL; +} + + +bool Handle::IssueRead() { + ScopedLock lock(this); + ASSERT(type_ != kListenSocket); + ASSERT(completion_port_ != INVALID_HANDLE_VALUE); + ASSERT(pending_read_ == NULL); + + IOBuffer* buffer = IOBuffer::AllocateReadBuffer(1024); + BOOL ok = ReadFile(handle_, + buffer->GetBufferStart(), + buffer->GetBufferSize(), + NULL, + buffer->GetCleanOverlapped()); + if (ok || GetLastError() == ERROR_IO_PENDING) { + // Completing asynchronously. + pending_read_ = buffer; + return true; + } + + fprintf(stderr, "ReadFile failed: %d\n", GetLastError()); + event_handler_->HandleClosed(this); + IOBuffer::DisposeBuffer(buffer); + return false; +} + + +bool Handle::IssueWrite() { + ScopedLock lock(this); + ASSERT(type_ != kListenSocket); + ASSERT(completion_port_ != INVALID_HANDLE_VALUE); + ASSERT(pending_write_ != NULL); + ASSERT(pending_write_->operation() == IOBuffer::kWrite); + + IOBuffer* buffer = pending_write_; + BOOL ok = WriteFile(handle_, + buffer->GetBufferStart(), + buffer->GetBufferSize(), + NULL, + buffer->GetCleanOverlapped()); + if (ok || GetLastError() == ERROR_IO_PENDING) { + // Completing asynchronously. + pending_write_ = buffer; + return true; + } + + fprintf(stderr, "WriteFile failed: %d\n", GetLastError()); + event_handler_->HandleClosed(this); + IOBuffer::DisposeBuffer(buffer); + return false; +} + + +bool ListenSocket::LoadAcceptEx() { + // Load the AcceptEx function into memory using WSAIoctl. + // The WSAIoctl function is an extension of the ioctlsocket() + // function that can use overlapped I/O. The function's 3rd + // through 6th parameters are input and output buffers where + // we pass the pointer to our AcceptEx function. This is used + // so that we can call the AcceptEx function directly, rather + // than refer to the Mswsock.lib library. + GUID guid_accept_ex = WSAID_ACCEPTEX; + DWORD bytes; + int status = WSAIoctl(socket(), + SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid_accept_ex, + sizeof(guid_accept_ex), + &AcceptEx_, + sizeof(AcceptEx_), + &bytes, + NULL, + NULL); + if (status == SOCKET_ERROR) { + fprintf(stderr, "Error WSAIoctl failed: %d\n", WSAGetLastError()); + return false; + } + return true; +} + + +bool ListenSocket::IssueAccept() { + ScopedLock lock(this); + + // For AcceptEx there needs to be buffer storage for address + // information for two addresses (local and remote address). The + // AcceptEx documentation says: "This value must be at least 16 + // bytes more than the maximum address length for the transport + // protocol in use." + static const int kAcceptExAddressAdditionalBytes = 16; + static const int kAcceptExAddressStorageSize = + sizeof(SOCKADDR_STORAGE) + kAcceptExAddressAdditionalBytes; + IOBuffer* buffer = + IOBuffer::AllocateAcceptBuffer(2 * kAcceptExAddressStorageSize); + DWORD received; + BOOL ok; + ok = AcceptEx_(socket(), + buffer->client(), + buffer->GetBufferStart(), + 0, // For now don't receive data with accept. + kAcceptExAddressStorageSize, + kAcceptExAddressStorageSize, + &received, + buffer->GetCleanOverlapped()); + if (!ok) { + if (WSAGetLastError() != WSA_IO_PENDING) { + fprintf(stderr, "AcceptEx failed: %d\n", WSAGetLastError()); + closesocket(buffer->client()); + IOBuffer::DisposeBuffer(buffer); + return false; + } + } + + pending_accept_count_++; + + return true; +} + + +void ListenSocket::AcceptComplete(IOBuffer* buffer, HANDLE completion_port) { + ScopedLock lock(this); + if (!closing_) { + ClientSocket* client_socket = new ClientSocket(buffer->client(), 0); + client_socket->CreateCompletionPort(completion_port); + if (accepted_head_ == NULL) { + accepted_head_ = client_socket; + accepted_tail_ = client_socket; + } else { + ASSERT(accepted_tail_ != NULL); + accepted_tail_->set_next(client_socket); + accepted_tail_ = client_socket; + } + } else { + closesocket(buffer->client()); + } + pending_accept_count_--; + IOBuffer::DisposeBuffer(buffer); +} + + +ClientSocket* ListenSocket::Accept() { + ScopedLock lock(this); + if (accepted_head_ == NULL) return NULL; + ClientSocket* result = accepted_head_; + accepted_head_ = accepted_head_->next(); + if (accepted_head_ == NULL) accepted_tail_ = NULL; + return result; +} + + +void ListenSocket::EnsureInitialized( + EventHandlerImplementation* event_handler) { + ScopedLock lock(this); + if (AcceptEx_ == NULL) { + ASSERT(completion_port_ == INVALID_HANDLE_VALUE); + ASSERT(event_handler_ == NULL); + event_handler_ = event_handler; + CreateCompletionPort(event_handler_->completion_port()); + LoadAcceptEx(); + } +} + + +void ListenSocket::AfterClose() { + ScopedLock lock(this); + while (true) { + // Get rid of connections already accepted. + ClientSocket *client = Accept(); + if (client != NULL) { + client->close(); + } else { + break; + } + } +} + + +bool ListenSocket::IsClosed() { + return closing_ && !HasPendingAccept(); +} + + +int ClientSocket::Available() { + ScopedLock lock(this); + if (data_ready_ == NULL) return 0; + ASSERT(!data_ready_->IsEmpty()); + return data_ready_->GetRemainingLength(); +} + + +int ClientSocket::Read(void* buffer, int num_bytes) { + ScopedLock lock(this); + if (data_ready_ == NULL) return 0; + num_bytes = data_ready_->Read(buffer, num_bytes); + if (data_ready_->IsEmpty()) { + IOBuffer::DisposeBuffer(data_ready_); + data_ready_ = NULL; + } + return num_bytes; +} + + +int ClientSocket::Write(const void* buffer, int num_bytes) { + ScopedLock lock(this); + if (pending_write_ != NULL) return 0; + if (completion_port_ == INVALID_HANDLE_VALUE) return 0; + if (num_bytes > 4096) num_bytes = 4096; + pending_write_ = IOBuffer::AllocateWriteBuffer(num_bytes); + pending_write_->Write(buffer, num_bytes); + IssueWrite(); + return num_bytes; +} + + +bool ClientSocket::IssueRead() { + ScopedLock lock(this); + ASSERT(completion_port_ != INVALID_HANDLE_VALUE); + ASSERT(pending_read_ == NULL); + + IOBuffer* buffer = IOBuffer::AllocateReadBuffer(1024); + + DWORD flags; + flags = 0; + int rc = WSARecv(socket(), + buffer->GetWASBUF(), + 1, + NULL, + &flags, + buffer->GetCleanOverlapped(), + NULL); + if (rc == NO_ERROR || WSAGetLastError() == WSA_IO_PENDING) { + pending_read_ = buffer; + return true; + } + + if (WSAGetLastError() != WSAECONNRESET) { + fprintf(stderr, "WSARecv failed: %d\n", WSAGetLastError()); + } + event_handler_->HandleClosed(this); + IOBuffer::DisposeBuffer(buffer); + return false; +} + + +bool ClientSocket::IssueWrite() { + ScopedLock lock(this); + ASSERT(completion_port_ != INVALID_HANDLE_VALUE); + ASSERT(pending_write_ != NULL); + ASSERT(pending_write_->operation() == IOBuffer::kWrite); + + int rc = WSASend(socket(), + pending_write_->GetWASBUF(), + 1, + NULL, + 0, + pending_write_->GetCleanOverlapped(), + NULL); + if (rc == NO_ERROR || WSAGetLastError() == WSA_IO_PENDING) { + return true; + } + + fprintf(stderr, "WSASend failed: %d\n", WSAGetLastError()); + IOBuffer::DisposeBuffer(pending_write_); + pending_write_ = NULL; + return false; +} + + +void ClientSocket::EnsureInitialized( + EventHandlerImplementation* event_handler) { + ScopedLock lock(this); + if (completion_port_ == INVALID_HANDLE_VALUE) { + ASSERT(event_handler_ == NULL); + event_handler_ = event_handler; + CreateCompletionPort(event_handler_->completion_port()); + } +} + + +void ClientSocket::AfterClose() { + ScopedLock lock(this); + if (data_ready_ != NULL) { + IOBuffer::DisposeBuffer(data_ready_); + data_ready_ = NULL; + } +} + + +bool ClientSocket::IsClosed() { + return closing_ && !HasPendingRead() && !HasPendingWrite(); +} + + +void EventHandlerImplementation::HandleInterrupt(InterruptMessage* msg) { + if (msg->id == -1) { + // Change of timeout request. Just set the new timeout and port as the + // completion thread will use the new timeout value for its next wait. + timeout_ = msg->data; + timeout_port_ = msg->dart_port; + } else { + bool delete_socket = false; + Handle* socket_desc = + reinterpret_cast(msg->id); + ASSERT(socket_desc != NULL); + if (socket_desc->is_listen_socket()) { + ListenSocket* listen_socket = + reinterpret_cast(socket_desc); + listen_socket->EnsureInitialized(this); + listen_socket->SetPortAndMask(msg->dart_port, msg->data); + + Handle::ScopedLock lock(listen_socket); + + // If incomming connections are requested make sure that pending accepts + // are issued. + if ((msg->data & (1 << kInEvent)) != 0) { + while (listen_socket->pending_accept_count() < 5) { + listen_socket->IssueAccept(); + } + } + + if ((msg->data & (1 << kCloseCommand)) != 0) { + listen_socket->close(); + if (listen_socket->IsClosed()) { + delete_socket = true; + } + } + } else { + ClientSocket* client_socket = + reinterpret_cast(socket_desc); + client_socket->SetPortAndMask(msg->dart_port, msg->data); + client_socket->EnsureInitialized(this); + + Handle::ScopedLock lock(client_socket); + + // If data available callback has been requested and data are + // available post it immediately. Otherwise make sure that a pending + // read is issued. + if ((msg->data & (1 << kInEvent)) != 0) { + if (client_socket->Available() > 0) { + int event_mask = (1 << kInEvent); + Dart_PostIntArray(client_socket->port(), 1, &event_mask); + } else if (!client_socket->HasPendingRead()) { + client_socket->IssueRead(); + } + } + + // If can send callback had been requested and there is no pending + // send post it immediately. + if ((msg->data & (1 << kOutEvent)) != 0) { + if (!client_socket->HasPendingWrite()) { + int event_mask = (1 << kOutEvent); + Dart_PostIntArray(client_socket->port(), 1, &event_mask); + } + } + + if ((msg->data & (1 << kCloseCommand)) != 0) { + client_socket->close(); + if (client_socket->IsClosed()) { + delete_socket = true; + } + } + } + if (delete_socket) { + delete socket_desc; + } + } +} + + +void EventHandlerImplementation::HandleAccept(ListenSocket* listen_socket, + IOBuffer* buffer) { + listen_socket->AcceptComplete(buffer, completion_port_); + + if (!listen_socket->is_closing()) { + int event_mask = 1 << kInEvent; + if ((listen_socket->mask() & event_mask) != 0) { + Dart_PostIntArray(listen_socket->port(), 1, &event_mask); + } + } + + if (listen_socket->IsClosed()) { + delete listen_socket; + } +} + + +void EventHandlerImplementation::HandleClosed(Handle* handle) { + if (!handle->is_closing()) { + int event_mask = 1 << kCloseEvent; + if ((handle->mask() & event_mask) != 0) { + Dart_PostIntArray(handle->port(), 1, &event_mask); + } + } +} + + +void EventHandlerImplementation::HandleRead(ClientSocket* client_socket, + int bytes, + IOBuffer* buffer) { + buffer->set_data_length(bytes); + client_socket->ReadComplete(buffer); + + if (bytes > 0) { + if (!client_socket->is_closing()) { + int event_mask = 1 << kInEvent; + if ((client_socket->mask() & event_mask) != 0) { + Dart_PostIntArray(client_socket->port(), 1, &event_mask); + } + } + } else { + ASSERT(bytes == 0); + HandleClosed(client_socket); + } + + if (client_socket->IsClosed()) { + delete client_socket; + } +} + + +void EventHandlerImplementation::HandleWrite(ClientSocket* client_socket, + int bytes, + IOBuffer* buffer) { + client_socket->WriteComplete(buffer); + + if (bytes > 0) { + if (!client_socket->is_closing()) { + int event_mask = 1 << kOutEvent; + if ((client_socket->mask() & event_mask) != 0) { + Dart_PostIntArray(client_socket->port(), 1, &event_mask); + } + } + } else { + ASSERT(bytes == 0); + HandleClosed(client_socket); + } + + if (client_socket->IsClosed()) { + delete client_socket; + } +} + + +void EventHandlerImplementation::HandleTimeout() { + // TODO(sgjesse) check if there actually is a timeout. + Dart_PostIntArray(timeout_port_, 0, NULL); + timeout_ = kInfinityTimeout; + timeout_port_ = 0; +} + + +void EventHandlerImplementation::HandleIOCompletion(DWORD bytes, + ULONG_PTR key, + OVERLAPPED* overlapped) { + IOBuffer* buffer = IOBuffer::GetFromOverlapped(overlapped); + switch (buffer->operation()) { + case IOBuffer::kAccept: { + ListenSocket* listen_socket = reinterpret_cast(key); + HandleAccept(listen_socket, buffer); + break; + } + case IOBuffer::kRead: { + ClientSocket* client_socket = reinterpret_cast(key); + HandleRead(client_socket, bytes, buffer); + break; + } + case IOBuffer::kWrite: { + ClientSocket* client_socket = reinterpret_cast(key); + HandleWrite(client_socket, bytes, buffer); + break; + } + default: + UNREACHABLE(); + } +} + + +EventHandlerImplementation::EventHandlerImplementation() { + intptr_t result; + completion_port_ = + CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1); + if (completion_port_ == NULL) { + FATAL("Completion port creation failed"); + } + timeout_ = kInfinityTimeout; + timeout_port_ = 0; +} + + +DWORD EventHandlerImplementation::GetTimeout() { + if (timeout_ == kInfinityTimeout) { + return kInfinityTimeout; + } + intptr_t millis = timeout_ - GetCurrentTimeMilliseconds(); + return (millis < 0) ? 0 : millis; +} + + +void EventHandlerImplementation::SendData(intptr_t id, + Dart_Port dart_port, + intptr_t data) { + InterruptMessage* msg = new InterruptMessage; + msg->id = id; + msg->dart_port = dart_port; + msg->data = data; + BOOL ok = PostQueuedCompletionStatus( + completion_port_, 0, NULL, reinterpret_cast(msg)); + if (!ok) { + FATAL("PostQueuedCompletionStatus failed"); + } +} + + +static unsigned int __stdcall EventHandlerThread(void* args) { + EventHandlerImplementation* handler = + reinterpret_cast(args); + while (true) { + DWORD bytes; + ULONG_PTR key; + OVERLAPPED* overlapped; + intptr_t millis = handler->GetTimeout(); + BOOL ok = GetQueuedCompletionStatus(handler->completion_port(), + &bytes, + &key, + &overlapped, + millis); + if (!ok && overlapped == NULL) { + if (GetLastError() == ERROR_ABANDONED_WAIT_0) { + // The completion port should never be closed. + printf("Completion port closed\n"); + UNREACHABLE(); + } else { + // Timeout is signalled by false result and NULL in overlapped. + handler->HandleTimeout(); + } + } else if (!ok) { + // If GetQueuedCompletionStatus return false and overlapped is + // not NULL then it did dequeue a request which failed. + if (overlapped != NULL) { + // Treat ERROR_CONNECTION_ABORTED as connection closed. + // The error ERROR_OPERATION_ABORTED is set for pending + // accept requests for a listen socket which is closed. + if (GetLastError() == ERROR_CONNECTION_ABORTED || + GetLastError() == ERROR_OPERATION_ABORTED) { + ASSERT(bytes == 0); + handler->HandleIOCompletion(bytes, key, overlapped); + } else { + printf("After GetQueuedCompletionStatus %d\n", GetLastError()); + UNREACHABLE(); + } + } else { + printf("After GetQueuedCompletionStatus %d\n", GetLastError()); + UNREACHABLE(); + } + } else if (key == NULL) { + // A key of NULL signals an interrupt message. + InterruptMessage* msg = reinterpret_cast(overlapped); + handler->HandleInterrupt(msg); + delete msg; + } else { + handler->HandleIOCompletion(bytes, key, overlapped); + } + } +} + + +void EventHandlerImplementation::StartEventHandler() { + uint32_t tid; + uintptr_t thread_handle = + _beginthreadex(NULL, 32 * 1024, EventHandlerThread, this, 0, &tid); + if (thread_handle == -1) { + FATAL("Failed to start event handler thread"); + } + + // Initialize Winsock32 + if (!Socket::Initialize()) { + FATAL("Failed to initialized Windows sockets"); + } +} diff --git a/runtime/bin/eventhandler_win.h b/runtime/bin/eventhandler_win.h new file mode 100644 index 00000000000..c61629bf7aa --- /dev/null +++ b/runtime/bin/eventhandler_win.h @@ -0,0 +1,333 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_EVENTHANDLER_WIN_H_ +#define BIN_EVENTHANDLER_WIN_H_ + +#include +#include + +#include "bin/builtin.h" + + +// Forward declarations. +class EventHandlerImplementation; +class Handle; +class FileHandle; +class SocketHandle; +class ClientSocket; +class ListenSocket; + + +struct InterruptMessage { + intptr_t id; + Dart_Port dart_port; + int64_t data; +}; + + +// An IOBuffer encapsulates the OVERLAPPED structure and the +// associated data buffer. For accept it also contains the pre-created +// socket for the client. +class IOBuffer { + public: + enum Operation { kAccept, kRead, kWrite }; + + static IOBuffer* AllocateAcceptBuffer(int buffer_size); + static IOBuffer* AllocateReadBuffer(int buffer_size); + static IOBuffer* AllocateWriteBuffer(int buffer_size); + static void DisposeBuffer(IOBuffer* buffer); + + // Find the IO buffer from the OVERLAPPED address. + static IOBuffer* GetFromOverlapped(OVERLAPPED* overlapped); + + // Read data from a buffer which has been received. It will read up + // to num_bytes bytes of data returning the actual number of bytes + // read. This will update the index of the next byte in the buffer + // so calling Read several times will keep returning new data from + // the buffer until all data have been read. + int Read(void* buffer, int num_bytes); + + // Write data to a buffer before sending it. Returns the number of bytes + // actually written to the buffer. Calls to Write will always write to + // the buffer from the begining. + int Write(const void* buffer, int num_bytes); + + // Check the amount of data in a read buffer which has not been read yet. + int GetRemainingLength(); + bool IsEmpty() { return GetRemainingLength() == 0; } + + Operation operation() { return operation_; } + SOCKET client() { return client_; } + char* GetBufferStart() { return reinterpret_cast(&buffer_data_); } + int GetBufferSize() { return buflen_; } + + // Returns the address of the OVERLAPPED structure with all fields + // initialized to zero. + OVERLAPPED* GetCleanOverlapped() { + memset(&overlapped_, 0, sizeof(overlapped_)); + return &overlapped_; + } + + // Returns a WASBUF structure initialized with the data in this IO buffer. + WSABUF* GetWASBUF() { + wbuf_.buf = GetBufferStart(); + wbuf_.len = GetBufferSize(); + return &wbuf_; + }; + + void set_data_length(int data_length) { data_length_ = data_length; } + + private: + IOBuffer(int buffer_size, Operation operation) + : operation_(operation), buflen_(buffer_size) { + memset(GetBufferStart(), 0, GetBufferSize()); + index_ = 0; + data_length_ = 0; + if (operation_ == kAccept) { + client_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + } + } + + void* operator new(size_t size, int buffer_size) { + return malloc(size + buffer_size); + } + + void operator delete(void* buffer) { + free(buffer); + } + + static IOBuffer* AllocateBuffer(int buffer_size, Operation operation); + + OVERLAPPED overlapped_; // OVERLAPPED structure for overlapped IO. + SOCKET client_; // Used for AcceptEx client socket. + int buflen_; // Length of the buffer. + Operation operation_; // Type of operation issued. + + int index_; // Index for next read from read buffer. + int data_length_; // Length of the actual data in the buffer. + + WSABUF wbuf_; // Structure for passing buffer to WSA functions. + + // Buffer for recv/send/AcceptEx. This must be at the end of the + // object as the object is allocated larger than it's definition + // indicate to extend this array. + uint8_t buffer_data_[1]; +}; + + +// Abstract super class for holding information on listen and connected +// sockets. +class Handle { + public: + enum Type { kFile, kClientSocket, kListenSocket }; + + class ScopedLock { + public: + explicit ScopedLock(Handle* handle) + : handle_(handle) { + handle_->Lock(); + } + ~ScopedLock() { + handle_->Unlock(); + } + + private: + Handle* handle_; + }; + + virtual ~Handle(); + + // Internal interface used by the event handler. + virtual bool IssueRead(); + virtual bool IssueWrite(); + bool HasPendingRead(); + bool HasPendingWrite(); + void ReadComplete(IOBuffer* buffer); + void WriteComplete(IOBuffer* buffer); + + virtual void EnsureInitialized( + EventHandlerImplementation* event_handler) = 0; + + HANDLE handle() { return handle_; } + Dart_Port port() { return port_; } + EventHandlerImplementation* event_handler() { return event_handler_; } + + void Lock(); + void Unlock(); + + bool CreateCompletionPort(HANDLE completion_port); + + void close(); + virtual bool IsClosed() = 0; + + void SetPortAndMask(Dart_Port port, intptr_t mask) { + port_ = port; + mask_ = mask; + } + Type type() { return type_; } + bool is_file() { return type_ == kFile; } + bool is_socket() { return type_ == kListenSocket || type_ == kClientSocket; } + bool is_listen_socket() { return type_ == kListenSocket; } + bool is_client_socket() { return type_ == kClientSocket; } + intptr_t mask() { return mask_; } + + bool is_closing() { return closing_; } + + protected: + explicit Handle(HANDLE handle); + Handle(HANDLE handle, Dart_Port port); + + virtual void AfterClose() = 0; + + Type type_; + HANDLE handle_; + bool closing_; // Is this handle in the process of closing? + Dart_Port port_; // Dart port to communicate events for this socket. + intptr_t mask_; // Mask of events to report through the port. + HANDLE completion_port_; + CRITICAL_SECTION cs_; // Critical section protecting this object. + EventHandlerImplementation* event_handler_; + + IOBuffer* data_ready_; // IO buffer for data ready to be read. + IOBuffer* pending_read_; // IO buffer for pending read. + IOBuffer* pending_write_; // IO buffer for pending write +}; + + +class FileHandle : public Handle { + public: + explicit FileHandle(HANDLE handle) + : Handle(reinterpret_cast(handle)) { type_ = kFile; } + FileHandle(HANDLE handle, Dart_Port port) + : Handle(reinterpret_cast(handle), port) { type_ = kFile; } +}; + + +class SocketHandle : public Handle { + public: + SOCKET socket() { return reinterpret_cast(handle_); } + + protected: + explicit SocketHandle(SOCKET s) : Handle(reinterpret_cast(s)) {} + SocketHandle(SOCKET s, Dart_Port port) + : Handle(reinterpret_cast(s), port) {} +}; + + +// Information on listen sockets. +class ListenSocket : public SocketHandle { + public: + explicit ListenSocket(SOCKET s) : SocketHandle(s), + AcceptEx_(NULL), + pending_accept_count_(0), + accepted_head_(NULL), + accepted_tail_(NULL) { + type_ = kListenSocket; + } + virtual ~ListenSocket() { + ASSERT(!HasPendingAccept()); + ASSERT(accepted_head_ == NULL); + ASSERT(accepted_tail_ == NULL); + }; + + // Socket interface exposing normal socket operations. + ClientSocket* Accept(); + + // Internal interface used by the event handler. + bool HasPendingAccept() { return pending_accept_count_ > 0; } + bool IssueAccept(); + void AcceptComplete(IOBuffer* buffer, HANDLE completion_port); + + virtual void EnsureInitialized( + EventHandlerImplementation* event_handler); + virtual bool IsClosed(); + + int pending_accept_count() { return pending_accept_count_; } + + private: + bool LoadAcceptEx(); + virtual void AfterClose(); + + LPFN_ACCEPTEX AcceptEx_; + int pending_accept_count_; + // Linked list of accepted connections provided by completion code. Ready to + // be handed over through accept. + ClientSocket* accepted_head_; + ClientSocket* accepted_tail_; +}; + + +// Information on connected sockets. +class ClientSocket : public SocketHandle { + public: + explicit ClientSocket(SOCKET s) + : SocketHandle(s), + next_(NULL) { type_ = kClientSocket; } + + ClientSocket(SOCKET s, Dart_Port port) + : SocketHandle(s, port), + next_(NULL) { type_ = kClientSocket; } + + virtual ~ClientSocket() { + // Don't delete this object until all pending requests have been handled. + ASSERT(!HasPendingRead()); + ASSERT(!HasPendingWrite()); + ASSERT(next_ == NULL); + }; + + // Socket interface exposing normal socket operations. + int Available(); + int Read(void* buffer, int num_bytes); + int Write(const void* buffer, int num_bytes); + + // Internal interface used by the event handler. + virtual bool IssueRead(); + virtual bool IssueWrite(); + + virtual void EnsureInitialized( + EventHandlerImplementation* event_handler); + virtual bool IsClosed(); + + ClientSocket* next() { return next_; } + void set_next(ClientSocket* next) { next_ = next; } + + private: + virtual void AfterClose(); + + ClientSocket* next_; +}; + + +// Event handler. +class EventHandlerImplementation { + public: + EventHandlerImplementation(); + virtual ~EventHandlerImplementation() {} + + void SendData(intptr_t id, Dart_Port dart_port, intptr_t data); + void StartEventHandler(); + + DWORD GetTimeout(); + void HandleInterrupt(InterruptMessage* msg); + void HandleTimeout(); + void HandleAccept(ListenSocket* listen_socket, IOBuffer* buffer); + void HandleClosed(Handle* handle); + void HandleRead(ClientSocket* client_socket, int bytes, IOBuffer* buffer); + void HandleWrite(ClientSocket* client_socket, int bytes, IOBuffer* buffer); + void HandleClose(ClientSocket* client_socket); + void HandleIOCompletion(DWORD bytes, ULONG_PTR key, OVERLAPPED* overlapped); + + HANDLE completion_port() { return completion_port_; } + + private: + ClientSocket* client_sockets_head_; + + int64_t timeout_; // Time for next timeout. + Dart_Port timeout_port_; + HANDLE completion_port_; +}; + + +#endif // BIN_EVENTHANDLER_WIN_H_ diff --git a/runtime/bin/fdutils.h b/runtime/bin/fdutils.h new file mode 100644 index 00000000000..5b8d187bf29 --- /dev/null +++ b/runtime/bin/fdutils.h @@ -0,0 +1,41 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_FDUTILS_H_ +#define BIN_FDUTILS_H_ + +#include "bin/builtin.h" +#include "bin/globals.h" + +class FDUtils { + public: + static bool SetNonBlocking(intptr_t fd); + + // Checks whether the file descriptor is blocking. If the function + // returns true the value pointed to by is_blocking will be set to + // the blocking state of the file descriptor. If the function + // returns false the system call for checking the file descriptor + // failed and the value pointed to by is_blocking is not modified. + static bool IsBlocking(intptr_t fd, bool* is_blocking); + + static intptr_t AvailableBytes(intptr_t fd); + + // Reads the requested number of bytes from a file descriptor. This + // function will only return on short reads if an error occours in + // which case it returns -1 and errno is still valid. The file + // descriptor must be in blocking mode. + static ssize_t ReadFromBlocking(int fd, void* buffer, size_t count); + + // Writes the requested number of bytes to a file descriptor. This + // function will only return on short writes if an error occours in + // which case it returns -1 and errno is still valid. The file + // descriptor must be in blocking mode. + static ssize_t WriteToBlocking(int fd, const void* buffer, size_t count); + + private: + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(FDUtils); +}; + +#endif // BIN_FDUTILS_H_ diff --git a/runtime/bin/fdutils_linux.cc b/runtime/bin/fdutils_linux.cc new file mode 100644 index 00000000000..d92b6608c52 --- /dev/null +++ b/runtime/bin/fdutils_linux.cc @@ -0,0 +1,104 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include + +#include "bin/fdutils.h" + + +bool FDUtils::SetNonBlocking(intptr_t fd) { + intptr_t status; + status = fcntl(fd, F_GETFL); + if (status < 0) { + perror("fcntl F_GETFL failed"); + return false; + } + status = (status | O_NONBLOCK); + if (fcntl(fd, F_SETFL, status) < 0) { + perror("fcntl F_SETFL failed"); + return false; + } + return true; +} + + +bool FDUtils::IsBlocking(intptr_t fd, bool* is_blocking) { + intptr_t status; + status = fcntl(fd, F_GETFL); + if (status < 0) { + perror("fcntl F_GETFL failed"); + return false; + } + *is_blocking = (status & O_NONBLOCK) == 0; + return true; +} + + +intptr_t FDUtils::AvailableBytes(intptr_t fd) { + size_t available; + int result = ioctl(fd, FIONREAD, &available); + if (result < 0) { + return result; + } + return available; +} + + +ssize_t FDUtils::ReadFromBlocking(int fd, void* buffer, size_t count) { +#ifdef DEBUG + bool is_blocking = false; + ASSERT(FDUtils::IsBlocking(fd, &is_blocking)); + ASSERT(is_blocking); +#endif + size_t remaining = count; + char* buffer_pos = reinterpret_cast(buffer); + while (remaining > 0) { + ssize_t bytes_read = read(fd, buffer_pos, remaining); + if (bytes_read == 0) { + return count - remaining; + } else if (bytes_read == -1 && errno != EINTR) { + // Error codes EAGAIN and EWOULDBLOCK should only happen for non + // blocking file descriptors. + ASSERT(errno != EAGAIN && errno != EWOULDBLOCK); + return -1; + } else if (bytes_read > 0) { + remaining -= bytes_read; + buffer_pos += bytes_read; + } else { + ASSERT(errno == EINTR); + } + } + return count; +} + + +ssize_t FDUtils::WriteToBlocking(int fd, const void* buffer, size_t count) { +#ifdef DEBUG + bool is_blocking = false; + ASSERT(FDUtils::IsBlocking(fd, &is_blocking)); + ASSERT(is_blocking); +#endif + size_t remaining = count; + char* buffer_pos = const_cast(reinterpret_cast(buffer)); + while (remaining > 0) { + ssize_t bytes_written = write(fd, buffer_pos, remaining); + if (bytes_written == 0) { + return count - remaining; + } else if (bytes_written == -1 && errno != EINTR) { + // Error codes EAGAIN and EWOULDBLOCK should only happen for non + // blocking file descriptors. + ASSERT(errno != EAGAIN && errno != EWOULDBLOCK); + return -1; + } else if (bytes_written > 0) { + remaining -= bytes_written; + buffer_pos += bytes_written; + } else { + ASSERT(errno == EINTR); + } + } + return count; +} diff --git a/runtime/bin/fdutils_macos.cc b/runtime/bin/fdutils_macos.cc new file mode 100644 index 00000000000..d92b6608c52 --- /dev/null +++ b/runtime/bin/fdutils_macos.cc @@ -0,0 +1,104 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include + +#include "bin/fdutils.h" + + +bool FDUtils::SetNonBlocking(intptr_t fd) { + intptr_t status; + status = fcntl(fd, F_GETFL); + if (status < 0) { + perror("fcntl F_GETFL failed"); + return false; + } + status = (status | O_NONBLOCK); + if (fcntl(fd, F_SETFL, status) < 0) { + perror("fcntl F_SETFL failed"); + return false; + } + return true; +} + + +bool FDUtils::IsBlocking(intptr_t fd, bool* is_blocking) { + intptr_t status; + status = fcntl(fd, F_GETFL); + if (status < 0) { + perror("fcntl F_GETFL failed"); + return false; + } + *is_blocking = (status & O_NONBLOCK) == 0; + return true; +} + + +intptr_t FDUtils::AvailableBytes(intptr_t fd) { + size_t available; + int result = ioctl(fd, FIONREAD, &available); + if (result < 0) { + return result; + } + return available; +} + + +ssize_t FDUtils::ReadFromBlocking(int fd, void* buffer, size_t count) { +#ifdef DEBUG + bool is_blocking = false; + ASSERT(FDUtils::IsBlocking(fd, &is_blocking)); + ASSERT(is_blocking); +#endif + size_t remaining = count; + char* buffer_pos = reinterpret_cast(buffer); + while (remaining > 0) { + ssize_t bytes_read = read(fd, buffer_pos, remaining); + if (bytes_read == 0) { + return count - remaining; + } else if (bytes_read == -1 && errno != EINTR) { + // Error codes EAGAIN and EWOULDBLOCK should only happen for non + // blocking file descriptors. + ASSERT(errno != EAGAIN && errno != EWOULDBLOCK); + return -1; + } else if (bytes_read > 0) { + remaining -= bytes_read; + buffer_pos += bytes_read; + } else { + ASSERT(errno == EINTR); + } + } + return count; +} + + +ssize_t FDUtils::WriteToBlocking(int fd, const void* buffer, size_t count) { +#ifdef DEBUG + bool is_blocking = false; + ASSERT(FDUtils::IsBlocking(fd, &is_blocking)); + ASSERT(is_blocking); +#endif + size_t remaining = count; + char* buffer_pos = const_cast(reinterpret_cast(buffer)); + while (remaining > 0) { + ssize_t bytes_written = write(fd, buffer_pos, remaining); + if (bytes_written == 0) { + return count - remaining; + } else if (bytes_written == -1 && errno != EINTR) { + // Error codes EAGAIN and EWOULDBLOCK should only happen for non + // blocking file descriptors. + ASSERT(errno != EAGAIN && errno != EWOULDBLOCK); + return -1; + } else if (bytes_written > 0) { + remaining -= bytes_written; + buffer_pos += bytes_written; + } else { + ASSERT(errno == EINTR); + } + } + return count; +} diff --git a/runtime/bin/file.cc b/runtime/bin/file.cc new file mode 100644 index 00000000000..7f590beb71a --- /dev/null +++ b/runtime/bin/file.cc @@ -0,0 +1,243 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "bin/file.h" + +#include "bin/builtin.h" +#include "bin/dartutils.h" + +#include "include/dart_api.h" + + +static const int kFileFieldIndex = 0; + + +bool File::ReadFully(void* buffer, int64_t num_bytes) { + int64_t remaining = num_bytes; + char* current_buffer = reinterpret_cast(buffer); + while (remaining > 0) { + int bytes_read = Read(current_buffer, remaining); + if (bytes_read <= 0) { + return false; + } + remaining -= bytes_read; // Reduce the number of remaining bytes. + current_buffer += bytes_read; // Move the buffer forward. + } + return true; +} + + +bool File::WriteFully(const void* buffer, int64_t num_bytes) { + int64_t remaining = num_bytes; + const char* current_buffer = reinterpret_cast(buffer); + while (remaining > 0) { + int bytes_read = Write(current_buffer, remaining); + if (bytes_read < 0) { + return false; + } + remaining -= bytes_read; // Reduce the number of remaining bytes. + current_buffer += bytes_read; // Move the buffer forward. + } + return true; +} + + +static File* GetFileHandle(Dart_Handle fileobj) { + Dart_Result result = Dart_GetNativeInstanceField(fileobj, kFileFieldIndex); + assert(Dart_IsValidResult(result)); + File* file = reinterpret_cast(Dart_GetResultAsCIntptr(result)); + return file; +} + + +void FUNCTION_NAME(File_OpenFile)(Dart_NativeArguments args) { + Dart_EnterScope(); + Dart_Handle fileobj = Dart_GetNativeArgument(args, 0); + const char* filename = + DartUtils::GetStringValue(Dart_GetNativeArgument(args, 1)); + bool writable = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 2)); + File* file = File::OpenFile(filename, writable); + Dart_SetNativeInstanceField(fileobj, + kFileFieldIndex, + reinterpret_cast(file)); + Dart_SetReturnValue(args, Dart_NewBoolean(file != NULL)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(File_Exists)(Dart_NativeArguments args) { + Dart_EnterScope(); + const char* filename = + DartUtils::GetStringValue(Dart_GetNativeArgument(args, 0)); + bool exists = File::FileExists(filename); + Dart_SetReturnValue(args, Dart_NewBoolean(exists)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(File_Close)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t return_value = -1; + Dart_Handle fileobj = Dart_GetNativeArgument(args, 0); + File* file = GetFileHandle(fileobj); + if (file != NULL) { + Dart_SetNativeInstanceField(fileobj, + kFileFieldIndex, + NULL); + delete file; + return_value = 0; + } + Dart_SetReturnValue(args, Dart_NewInteger(return_value)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(File_ReadByte)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t return_value = -1; + File* file = GetFileHandle(Dart_GetNativeArgument(args, 0)); + if (file != NULL) { + uint8_t buffer; + int bytes_read = file->Read(reinterpret_cast(&buffer), 1); + if (bytes_read >= 0) { + return_value = static_cast(buffer); + } + } + Dart_SetReturnValue(args, Dart_NewInteger(return_value)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(File_WriteByte)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t return_value = -1; + File* file = GetFileHandle(Dart_GetNativeArgument(args, 0)); + if (file != NULL) { + int64_t value = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1)); + uint8_t buffer = static_cast(value & 0xff); + int bytes_written = file->Write(reinterpret_cast(&buffer), 1); + if (bytes_written >= 0) { + return_value = bytes_written; + } + } + Dart_SetReturnValue(args, Dart_NewInteger(return_value)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(File_WriteString)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t return_value = -1; + File* file = GetFileHandle(Dart_GetNativeArgument(args, 0)); + if (file != NULL) { + const char* str = + DartUtils::GetStringValue(Dart_GetNativeArgument(args, 1)); + int bytes_written = file->Write(reinterpret_cast(str), + strlen(str)); + if (bytes_written >= 0) { + return_value = bytes_written; + } + } + Dart_SetReturnValue(args, Dart_NewInteger(return_value)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(File_ReadList)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t return_value = -1; + File* file = GetFileHandle(Dart_GetNativeArgument(args, 0)); + if (file != NULL) { + Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1); + assert(Dart_IsArray(buffer_obj)); + int64_t offset = + DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2)); + int64_t length = + DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 3)); + Dart_Result result = Dart_GetLength(buffer_obj); + assert(Dart_IsValidResult(result)); + assert((offset + length) <= Dart_GetResultAsCIntptr(result)); + uint8_t* buffer = new uint8_t[length]; + int total_bytes_read = + file->Read(reinterpret_cast(buffer), length); + /* + * Reading 0 indicates end of file. + */ + if (total_bytes_read >= 0) { + result = + Dart_ArraySet(buffer_obj, offset, buffer, total_bytes_read); + ASSERT(Dart_IsValidResult(result)); + return_value = total_bytes_read; + } + delete[] buffer; + } + Dart_SetReturnValue(args, Dart_NewInteger(return_value)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(File_WriteList)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t return_value = -1; + File* file = GetFileHandle(Dart_GetNativeArgument(args, 0)); + if (file != NULL) { + Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1); + assert(Dart_IsArray(buffer_obj)); + int64_t offset = + DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2)); + int64_t length = + DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 3)); + Dart_Result result = Dart_GetLength(buffer_obj); + assert(Dart_IsValidResult(result)); + assert((offset + length) <= Dart_GetResultAsCIntptr(result)); + uint8_t* buffer = new uint8_t[length]; + result = Dart_ArrayGet(buffer_obj, offset, buffer, length); + ASSERT(Dart_IsValidResult(result)); + int total_bytes_written = + file->Write(reinterpret_cast(buffer), length); + if (total_bytes_written >= 0) { + return_value = total_bytes_written; + } + delete[] buffer; + } + Dart_SetReturnValue(args, Dart_NewInteger(return_value)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(File_Position)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t return_value = -1; + File* file = GetFileHandle(Dart_GetNativeArgument(args, 0)); + if (file != NULL) { + return_value = file->Position(); + } + Dart_SetReturnValue(args, Dart_NewInteger(return_value)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(File_Length)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t return_value = -1; + File* file = GetFileHandle(Dart_GetNativeArgument(args, 0)); + if (file != NULL) { + return_value = file->Length(); + } + Dart_SetReturnValue(args, Dart_NewInteger(return_value)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(File_Flush)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t return_value = -1; + File* file = GetFileHandle(Dart_GetNativeArgument(args, 0)); + if (file != NULL) { + file->Flush(); + return_value = 0; + } + Dart_SetReturnValue(args, Dart_NewInteger(return_value)); + Dart_ExitScope(); +} diff --git a/runtime/bin/file.dart b/runtime/bin/file.dart new file mode 100644 index 00000000000..4ca1e6e310c --- /dev/null +++ b/runtime/bin/file.dart @@ -0,0 +1,65 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class FileIOException implements Exception { + const FileIOException([String message = ""]) : _message = message; + String getMessage() { return _message; } + final String _message; +} + +interface FileInputStream extends InputStream factory _FileInputStream { + FileInputStream(File file); +} + +interface FileOutputStream extends OutputStream factory _FileOutputStream { + FileOutputStream(File file); +} + +interface File factory _File { + // Open a file. + File(String name, bool writable); + + // Close the file. + void close(); + + // Synchronously read a single byte from the file. + // TODO(jrgfogh): Remove this call. + int readByte(); + + // Synchronously write a single byte to the file. + // TODO(jrgfogh): Remove this call. + int writeByte(int value); + + // Synchronously write a single string to the file. + // TODO(jrgfogh): Remove this call. + int writeString(String string); + + // Synchronously read a List from the file. + // TODO(jrgfogh): Remove this call. + int readList(List buffer, int offset, int bytes); + + // Synchronously write a List to the file. + // TODO(jrgfogh): Remove this call. + int writeList(List buffer, int offset, int bytes); + + // The current position of the file handle. + int get position(); + + // The length of the file. + int get length(); + + // Flush the contents of the file to disk. + void flush(); + + // Each file has an unique InputStream. + InputStream get inputStream(); + + // Each file has an unique OutputStream. + OutputStream get outputStream(); +} + + +class FileUtil { + static bool fileExists(String name) native "File_Exists"; +} diff --git a/runtime/bin/file.h b/runtime/bin/file.h new file mode 100644 index 00000000000..26b1fc5bdea --- /dev/null +++ b/runtime/bin/file.h @@ -0,0 +1,78 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_FILE_H_ +#define BIN_FILE_H_ + +#if defined(_WIN32) +typedef signed __int64 int64_t; +typedef unsigned __int8 uint8_t; +#else +#include +#endif + +#include +#include +#include +#include +#include + +// Forward declaration. +class FileHandle; + +class File { + public: + ~File(); + + // Read/Write attempt to transfer num_bytes to/from buffer. It returns + // the number of bytes read/written. + int64_t Read(void* buffer, int64_t num_bytes); + int64_t Write(const void* buffer, int64_t num_bytes); + + // ReadFully and WriteFully do attempt to transfer num_bytes to/from + // the buffer. In the event of short accesses they will loop internally until + // the whole buffer has been transferred or an error occurs. If an error + // occurred the result will be set to false. + bool ReadFully(void* buffer, int64_t num_bytes); + bool WriteFully(const void* buffer, int64_t num_bytes); + bool WriteByte(uint8_t byte) { + return WriteFully(&byte, 1); + } + + // Get the length of the file. Returns a negative value if the length cannot + // be determined (e.g. not seekable device). + off_t Length(); + + // Get the current position in the file. + // Returns a negative value if position cannot be determined. + off_t Position(); + + // Flush contents of file. + void Flush(); + + const char* name() const { return name_; } + + static File* OpenFile(const char* name, bool writable); + static bool FileExists(const char* name); + static bool IsAbsolutePath(const char* pathname); + static const char* PathSeparator(); + static const char* StringEscapedPathSeparator(); + + private: + File(const char* name, FileHandle* handle) : name_(name), handle_(handle) { } + void Close(); + bool IsClosed(); + + static const int kClosedFd = -1; + + const char* name_; + // FileHandle is an OS specific class which stores data about the file. + FileHandle* handle_; // OS specific handle for the file. + + // DISALLOW_COPY_AND_ASSIGN(File). + File(const File&); + void operator=(const File&); +}; + +#endif // BIN_FILE_H_ diff --git a/runtime/bin/file_impl.dart b/runtime/bin/file_impl.dart new file mode 100644 index 00000000000..58da22fef15 --- /dev/null +++ b/runtime/bin/file_impl.dart @@ -0,0 +1,223 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class _FileInputStream implements FileInputStream { + _FileInputStream(File file) { + _file = file; + } + + bool read(List buffer, int offset, int len, void callback()) { + int bytesRead = _file.readList(buffer, offset, len); + + if (bytesRead == len) { + if (callback != null) { + callback(); + } + return true; + } else { + throw "FileInputStream: read error"; + } + } + + // TODO(srdjan): Reading whole file at once does not scale. Implement partial + // file reading and pattern checks. + void readUntil(List pattern, void callback(List buffer)) { + List buffer = new List(_file.length); + int result = _file.readList(buffer, 0, _file.length); + if (result > 0) { + int index = indexOf(buffer, pattern); + if (index != -1) { + int resultBufferSize = index + pattern.length; + List resultBuffer = new List(resultBufferSize); + resultBuffer.copyFrom(buffer, 0, 0, resultBufferSize); + callback(resultBuffer); + } + } + } + + // TODO(srdjan: move this method to Lists.dart (helper method). + static int indexOf(List buffer, List pattern) { + if (pattern.length == 0) { + return buffer.length; + } + int len = buffer.length - pattern.length + 1; + for (int index = 0; index < len; index++) { + bool match = true; + for (int k = 0; k < pattern.length; k++) { + if (buffer[index + k] != pattern[k]) { + match = false; + break; + } + } + if (match) { + return index; + } + } + return -1; + } + + File _file; +} + +class _FileOutputStream implements FileOutputStream { + _FileOutputStream(File file) { + _file = file; + } + + bool write(List buffer, int offset, int len, void callback()) { + int bytesWritten = _file.writeList(buffer, offset, len); + + if (bytesWritten == len) { + return true; + } else { + throw "FileOutputStream: write error"; + } + } + + File _file; +} + +// Class for encapsulating the native implementation of files. +class _File extends FileNativeWrapper implements File { + // Constructor for file. + factory _File(String name, bool writable) { + _File file = new _File._internal(); + if (!file._openFile(name, writable)) { + return null; + } + file._writeable = writable; + return file; + } + _File._internal(); + + bool _openFile(String name, bool writable) native "File_OpenFile"; + + void close() { + _close(); + } + int _close() native "File_Close"; + + int readByte() { + int result = _readByte(); + if (result == -1) { + throw new FileIOException("Error: readByte failed"); + } + return result; + } + int _readByte() native "File_ReadByte"; + + int writeByte(int value) { + int result = _writeByte(value); + if (result == -1) { + throw new FileIOException("Error: writeByte failed"); + } + return result; + } + int _writeByte(int value) native "File_WriteByte"; + + int writeString(String string) { + int result = _writeString(string); + if (result == -1) { + throw new FileIOException("Error: writeString failed"); + } + return result; + } + int _writeString(String string) native "File_WriteString"; + + int readList(List buffer, int offset, int bytes) { + if (bytes == 0) { + return 0; + } + if (offset < 0) { + throw new IndexOutOfRangeException(offset); + } + if (bytes < 0) { + throw new IndexOutOfRangeException(bytes); + } + if ((offset + bytes) > buffer.length) { + throw new IndexOutOfRangeException(offset + bytes); + } + int result = _readList(buffer, offset, bytes); + if (result == -1) { + throw new FileIOException("Error: readList failed"); + } + return result; + } + int _readList(List buffer, int offset, int bytes) + native "File_ReadList"; + + int writeList(List buffer, int offset, int bytes) { + if (bytes == 0) { + return 0; + } + if (offset < 0) { + throw new IndexOutOfRangeException(offset); + } + if (bytes < 0) { + throw new IndexOutOfRangeException(bytes); + } + if ((offset + bytes) > buffer.length) { + throw new IndexOutOfRangeException(offset + bytes); + } + int result = _writeList(buffer, offset, bytes); + if (result == -1) { + throw new FileIOException("Error: writeList failed"); + } + return result; + } + int _writeList(List buffer, int offset, int bytes) + native "File_WriteList"; + + int get position() { + int result = _position; + if (result == -1) { + throw new FileIOException("Error: get position failed"); + } + return result; + } + int get _position() native "File_Position"; + + int get length() { + int result = _length; + if (result == -1) { + throw new FileIOException("Error: get length failed"); + } + return result; + } + int get _length() native "File_Length"; + + void flush() { + int result = _flush(); + if (result == -1) { + throw new FileIOException("Error: flush failed"); + } + } + int _flush() native "File_Flush"; + + // Each file has an unique InputStream. + InputStream get inputStream() { + if (_inputStream === null) { + _inputStream = new FileInputStream(this); + } + return _inputStream; + } + + // Each file has an unique OutputStream. + OutputStream get outputStream() { + if (!_writeable) { + throw "File is not writable"; + } + if (_outputStream === null) { + _outputStream = new FileOutputStream(this); + } + return _outputStream; + } + + // Set of native methods used to provide file functionality. + static bool fileExists(String name) native "File_Exists"; + + bool _writeable; + InputStream _inputStream; + OutputStream _outputStream; +} diff --git a/runtime/bin/file_linux.cc b/runtime/bin/file_linux.cc new file mode 100644 index 00000000000..7a976070ea5 --- /dev/null +++ b/runtime/bin/file_linux.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + +#include "bin/file.h" + +class FileHandle { + public: + explicit FileHandle(int fd) : fd_(fd) { } + ~FileHandle() { } + int fd() const { return fd_; } + void set_fd(int fd) { fd_ = fd; } + + private: + int fd_; + + // DISALLOW_COPY_AND_ASSIGN(FileHandle). + FileHandle(const FileHandle&); + void operator=(const FileHandle&); +}; + + +File::~File() { + // Close the file (unless it's a standard stream). + if (handle_->fd() > STDERR_FILENO) { + Close(); + } + delete handle_; +} + + +void File::Close() { + assert(handle_->fd() >= 0); + int err = close(handle_->fd()); + if (err != 0) { + const int kBufferSize = 1024; + char error_message[kBufferSize]; + strerror_r(errno, error_message, kBufferSize); + fprintf(stderr, "%s\n", error_message); + } + handle_->set_fd(kClosedFd); +} + + +bool File::IsClosed() { + return handle_->fd() == kClosedFd; +} + + +int64_t File::Read(void* buffer, int64_t num_bytes) { + assert(handle_->fd() >= 0); + return read(handle_->fd(), buffer, num_bytes); +} + + +int64_t File::Write(const void* buffer, int64_t num_bytes) { + assert(handle_->fd() >= 0); + return write(handle_->fd(), buffer, num_bytes); +} + + +off_t File::Position() { + assert(handle_->fd() >= 0); + return lseek(handle_->fd(), 0, SEEK_CUR); +} + + +void File::Flush() { + assert(handle_->fd() >= 0); + fsync(handle_->fd()); +} + + +off_t File::Length() { + assert(handle_->fd() >= 0); + off_t position = lseek(handle_->fd(), 0, SEEK_CUR); + if (position < 0) { + // The file is not capable of seeking. Return an error. + return -1; + } + off_t result = lseek(handle_->fd(), 0, SEEK_END); + lseek(handle_->fd(), position, SEEK_SET); + return result; +} + + +File* File::OpenFile(const char* name, bool writable) { + int flags = O_RDONLY; + if (writable) { + flags = (O_RDWR | O_CREAT | O_TRUNC); + } + int fd = open(name, flags, 0666); + if (fd < 0) { + return NULL; + } + return new File(name, new FileHandle(fd)); +} + + +bool File::FileExists(const char* name) { + struct stat st; + if (stat(name, &st) == 0) { + return S_ISREG(st.st_mode); // Deal with symlinks? + } else { + return false; + } +} + + +bool File::IsAbsolutePath(const char* pathname) { + return (pathname != NULL && pathname[0] == '/'); +} + + +const char* File::PathSeparator() { + return "/"; +} + + +const char* File::StringEscapedPathSeparator() { + return "/"; +} diff --git a/runtime/bin/file_macos.cc b/runtime/bin/file_macos.cc new file mode 100644 index 00000000000..7a976070ea5 --- /dev/null +++ b/runtime/bin/file_macos.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + +#include "bin/file.h" + +class FileHandle { + public: + explicit FileHandle(int fd) : fd_(fd) { } + ~FileHandle() { } + int fd() const { return fd_; } + void set_fd(int fd) { fd_ = fd; } + + private: + int fd_; + + // DISALLOW_COPY_AND_ASSIGN(FileHandle). + FileHandle(const FileHandle&); + void operator=(const FileHandle&); +}; + + +File::~File() { + // Close the file (unless it's a standard stream). + if (handle_->fd() > STDERR_FILENO) { + Close(); + } + delete handle_; +} + + +void File::Close() { + assert(handle_->fd() >= 0); + int err = close(handle_->fd()); + if (err != 0) { + const int kBufferSize = 1024; + char error_message[kBufferSize]; + strerror_r(errno, error_message, kBufferSize); + fprintf(stderr, "%s\n", error_message); + } + handle_->set_fd(kClosedFd); +} + + +bool File::IsClosed() { + return handle_->fd() == kClosedFd; +} + + +int64_t File::Read(void* buffer, int64_t num_bytes) { + assert(handle_->fd() >= 0); + return read(handle_->fd(), buffer, num_bytes); +} + + +int64_t File::Write(const void* buffer, int64_t num_bytes) { + assert(handle_->fd() >= 0); + return write(handle_->fd(), buffer, num_bytes); +} + + +off_t File::Position() { + assert(handle_->fd() >= 0); + return lseek(handle_->fd(), 0, SEEK_CUR); +} + + +void File::Flush() { + assert(handle_->fd() >= 0); + fsync(handle_->fd()); +} + + +off_t File::Length() { + assert(handle_->fd() >= 0); + off_t position = lseek(handle_->fd(), 0, SEEK_CUR); + if (position < 0) { + // The file is not capable of seeking. Return an error. + return -1; + } + off_t result = lseek(handle_->fd(), 0, SEEK_END); + lseek(handle_->fd(), position, SEEK_SET); + return result; +} + + +File* File::OpenFile(const char* name, bool writable) { + int flags = O_RDONLY; + if (writable) { + flags = (O_RDWR | O_CREAT | O_TRUNC); + } + int fd = open(name, flags, 0666); + if (fd < 0) { + return NULL; + } + return new File(name, new FileHandle(fd)); +} + + +bool File::FileExists(const char* name) { + struct stat st; + if (stat(name, &st) == 0) { + return S_ISREG(st.st_mode); // Deal with symlinks? + } else { + return false; + } +} + + +bool File::IsAbsolutePath(const char* pathname) { + return (pathname != NULL && pathname[0] == '/'); +} + + +const char* File::PathSeparator() { + return "/"; +} + + +const char* File::StringEscapedPathSeparator() { + return "/"; +} diff --git a/runtime/bin/file_test.cc b/runtime/bin/file_test.cc new file mode 100644 index 00000000000..2caa8ec5305 --- /dev/null +++ b/runtime/bin/file_test.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "bin/file.h" + +#include "vm/assert.h" +#include "vm/globals.h" +#include "vm/unit_test.h" + + +// Helper method to be able to run the test from the runtime +// directory, or the top directory. +static const char* GetFileName(const char* name) { + if (File::FileExists(name)) { + return name; + } else { + static const int kRuntimeLength = strlen("runtime/"); + return name + kRuntimeLength; + } +} + + +UNIT_TEST_CASE(Read) { + const char* kFilename = GetFileName("runtime/bin/file_test.cc"); + File* file = File::OpenFile(kFilename, false); + EXPECT(file != NULL); + EXPECT_STREQ(kFilename, file->name()); + char buffer[16]; + buffer[0] = '\0'; + EXPECT(file->ReadFully(buffer, 13)); // ReadFully returns true. + buffer[13] = '\0'; + EXPECT_STREQ("// Copyright ", buffer); + EXPECT(!file->WriteByte(1)); // Cannot write to a read-only file. + delete file; +} + + +UNIT_TEST_CASE(FileLength) { + const char* kFilename = + GetFileName("runtime/tests/vm/data/fixed_length_file"); + File* file = File::OpenFile(kFilename, false); + EXPECT(file != NULL); + EXPECT_EQ(42, file->Length()); + delete file; +} + + +UNIT_TEST_CASE(FilePosition) { + char buf[42]; + const char* kFilename = + GetFileName("runtime/tests/vm/data/fixed_length_file"); + File* file = File::OpenFile(kFilename, false); + EXPECT(file != NULL); + EXPECT(file->ReadFully(buf, 12)); + EXPECT_EQ(12, file->Position()); + EXPECT(file->ReadFully(buf, 6)); + EXPECT_EQ(18, file->Position()); + delete file; +} diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc new file mode 100644 index 00000000000..3aa08f9d18f --- /dev/null +++ b/runtime/bin/file_win.cc @@ -0,0 +1,130 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + +#include "bin/builtin.h" +#include "bin/file.h" + +class FileHandle { + public: + explicit FileHandle(int fd) : fd_(fd) { } + ~FileHandle() { } + int fd() const { return fd_; } + void set_fd(int fd) { fd_ = fd; } + + private: + int fd_; + + // DISALLOW_COPY_AND_ASSIGN(FileHandle). + FileHandle(const FileHandle&); + void operator=(const FileHandle&); +}; + + +File::~File() { + // Close the file (unless it's a standard stream). + if (handle_->fd() > 2) { + Close(); + } + delete handle_; +} + + +void File::Close() { + assert(handle_->fd() >= 0); + int err = close(handle_->fd()); + if (err != 0) { + fprintf(stderr, "%s\n", strerror(errno)); + } + handle_->set_fd(kClosedFd); +} + + +bool File::IsClosed() { + return handle_->fd() == kClosedFd; +} + + +int64_t File::Read(void* buffer, int64_t num_bytes) { + assert(handle_->fd() >= 0); + return read(handle_->fd(), buffer, num_bytes); +} + + +int64_t File::Write(const void* buffer, int64_t num_bytes) { + assert(handle_->fd() >= 0); + return write(handle_->fd(), buffer, num_bytes); +} + + +off_t File::Position() { + assert(handle_->fd() >= 0); + return lseek(handle_->fd(), 0, SEEK_CUR); +} + + +void File::Flush() { + assert(handle_->fd()); + _commit(handle_->fd()); +} + + +off_t File::Length() { + assert(handle_->fd() >= 0); + off_t position = lseek(handle_->fd(), 0, SEEK_CUR); + if (position < 0) { + // The file is not capable of seeking. Return an error. + return -1; + } + off_t result = lseek(handle_->fd(), 0, SEEK_END); + lseek(handle_->fd(), position, SEEK_SET); + return result; +} + + +File* File::OpenFile(const char* name, bool writable) { + int flags = O_RDONLY | O_BINARY; + if (writable) { + flags = (O_RDWR | O_CREAT | O_TRUNC | O_BINARY); + } + int fd = open(name, flags, 0666); + if (fd < 0) { + return NULL; + } + return new File(name, new FileHandle(fd)); +} + + +bool File::FileExists(const char* name) { + struct stat st; + if (stat(name, &st) == 0) { + return ((st.st_mode & S_IFMT) == S_IFREG); + } else { + return false; + } +} + + +bool File::IsAbsolutePath(const char* pathname) { + // Should we consider network paths? + if (pathname == NULL) return false; + return (strlen(pathname) > 2) && + (pathname[1] == ':') && + (pathname[2] == '\\'); +} + + +const char* File::PathSeparator() { + return "\\"; +} + + +const char* File::StringEscapedPathSeparator() { + return "\\\\"; +} diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc new file mode 100644 index 00000000000..21a772e364f --- /dev/null +++ b/runtime/bin/gen_snapshot.cc @@ -0,0 +1,186 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Generate a snapshot file after loading all the scripts specified on the +// command line. + +#include +#include +#include +#include + +#include "include/dart_api.h" + +#include "bin/builtin.h" +#include "bin/file.h" +#include "bin/globals.h" +#include "bin/process_script.h" + +// Global state that indicates whether a snapshot is to be created and +// if so which file to write the snapshot into. +static const char* snapshot_filename = NULL; + +static bool IsValidFlag(const char* name, + const char* prefix, + intptr_t prefix_length) { + intptr_t name_length = strlen(name); + return ((name_length > prefix_length) && + (strncmp(name, prefix, prefix_length) == 0)); +} + + +static const char* ProcessOption(const char* option, const char* name) { + const intptr_t length = strlen(name); + if (strncmp(option, name, length) == 0) { + return (option + length); + } + return NULL; +} + + +static bool ProcessSnapshotOption(const char* option) { + const char* kSnapshotOption = "--snapshot="; + snapshot_filename = ProcessOption(option, kSnapshotOption); + return snapshot_filename != NULL; +} + + +// Parse out the command line arguments. Returns -1 if the arguments +// are incorrect, 0 otherwise. +static int ParseArguments(int argc, + char** argv, + CommandLineOptions* vm_options, + char** script_name) { + const char* kPrefix = "--"; + const intptr_t kPrefixLen = strlen(kPrefix); + + // Skip the binary name. + int i = 1; + + // Parse out the vm options. + while ((i < argc) && IsValidFlag(argv[i], kPrefix, kPrefixLen)) { + if (ProcessSnapshotOption(argv[i])) { + i += 1; + continue; + } + vm_options->AddArgument(argv[i]); + i += 1; + } + + // Get the script name. + if (i < argc) { + *script_name = argv[i]; + i += 1; + } else { + *script_name = NULL; + } + + return 0; +} + + +static void WriteSnapshotFile(const uint8_t* buffer, const intptr_t size) { + const bool kWritable = true; + File* file = File::OpenFile(snapshot_filename, kWritable); + ASSERT(file != NULL); + for (intptr_t i = 0; i < size; i++) { + file->WriteByte(buffer[i]); + } + delete file; +} + + +static void* SnapshotCreateCallback(void* data) { + const char* script_name = reinterpret_cast(data); + Dart_Result result; + Dart_EnterScope(); + + ASSERT(snapshot_filename != NULL); + + // If a file is specified on the command line, load it up before a snapshot + // is created. + if (script_name != NULL) { + // Load the specified script. + result = LoadScript(script_name); + if (!Dart_IsValidResult(result)) { + const char* err_msg = Dart_GetErrorCString(result); + fprintf(stderr, "Errors encountered while loading script: %s\n", err_msg); + Dart_ExitScope(); + exit(255); + } + + Dart_Handle library = Dart_GetResult(result); + if (!Dart_IsLibrary(library)) { + fprintf(stderr, + "Expected a library when loading script: %s", + script_name); + Dart_ExitScope(); + exit(255); + } + Builtin_ImportLibrary(library); + } else { + Builtin_LoadLibrary(); + } + // Setup the native resolver for built in library functions. + Builtin_SetNativeResolver(); + + uint8_t* buffer = NULL; + intptr_t size = 0; + // First create the snapshot. + result = Dart_CreateSnapshot(&buffer, &size); + if (!Dart_IsValidResult(result)) { + const char* err_msg = Dart_GetErrorCString(result); + fprintf(stderr, "Error while creating snapshot: %s\n", err_msg); + Dart_ExitScope(); + exit(255); + } + // Now write the snapshot out to specified file and exit. + WriteSnapshotFile(buffer, size); + Dart_ExitScope(); + return data; +} + + +static void PrintUsage() { + fprintf(stderr, + "dart [] " + "[]\n"); +} + + +int main(int argc, char** argv) { + CommandLineOptions vm_options(argc); + char* script_name; + + // Parse command line arguments. + if (ParseArguments(argc, + argv, + &vm_options, + &script_name) < 0) { + PrintUsage(); + return 255; + } + + if (snapshot_filename == NULL) { + fprintf(stderr, "No snapshot output file specified\n"); + return 255; + } + + // Initialize the Dart VM. + Dart_Initialize(vm_options.count(), + vm_options.arguments(), + SnapshotCreateCallback); + + // Create an isolate. As a side effect, SnapshotCreateCallback + // gets called, which loads the script (if one is specified), its libraries + // and writes out a snapshot. + Dart_Isolate isolate = Dart_CreateIsolate(NULL, script_name); + if (isolate == NULL) { + return 255; + } + + // Shutdown the isolate. + Dart_ShutdownIsolate(); + return 0; +} diff --git a/runtime/bin/globals.h b/runtime/bin/globals.h new file mode 100644 index 00000000000..9385aa2a0a7 --- /dev/null +++ b/runtime/bin/globals.h @@ -0,0 +1,126 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_GLOBALS_H_ +#define BIN_GLOBALS_H_ + +#if defined(_WIN32) +// Cut down on the amount of stuff that gets included via windows.h. +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#define NOKERNEL +#define NOUSER +#define NOSERVICE +#define NOSOUND +#define NOMCX + +#include +#endif + +// Processor architecture detection. For more info on what's defined, see: +// http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// http://www.agner.org/optimize/calling_conventions.pdf +// or with gcc, run: "echo | gcc -E -dM -" +#if defined(_M_X64) || defined(__x86_64__) +#define HOST_ARCH_X64 1 +#define ARCH_IS_64_BIT 1 +#elif defined(_M_IX86) || defined(__i386__) +#define HOST_ARCH_IA32 1 +#define ARCH_IS_32_BIT 1 +#elif defined(__ARMEL__) +#define HOST_ARCH_ARM 1 +#define ARCH_IS_32_BIT 1 +#else +#error Architecture was not detected as supported by Dart. +#endif + + +#if !defined(TARGET_ARCH_ARM) +#if !defined(TARGET_ARCH_X64) +#if !defined(TARGET_ARCH_IA32) +// No target architecture specified pick the one matching the host architecture. +#if defined(HOST_ARCH_ARM) +#define TARGET_ARCH_ARM 1 +#elif defined(HOST_ARCH_X64) +#define TARGET_ARCH_X64 1 +#elif defined(HOST_ARCH_IA32) +#define TARGET_ARCH_IA32 1 +#else +#error Automatic target architecture detection failed. +#endif +#endif +#endif +#endif + + +// Verify that host and target architectures match, we cannot +// have a 64 bit Dart VM generating 32 bit code or vice-versa. +#if defined(TARGET_ARCH_X64) +#if !defined(ARCH_IS_64_BIT) +#error Mismatched Host/Target architectures. +#endif +#elif defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) +#if !defined(ARCH_IS_32_BIT) +#error Mismatched Host/Target architectures. +#endif +#endif + + +// Target OS detection. +// for more information on predefined macros: +// - http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// - with gcc, run: "echo | gcc -E -dM -" +#if defined(__linux__) || defined(__FreeBSD__) +#define TARGET_OS_LINUX 1 +#elif defined(__APPLE__) +#define TARGET_OS_MACOS 1 +#elif defined(_WIN32) +#define TARGET_OS_WINDOWS 1 +#else +#error Automatic target os detection failed. +#endif + + +// A macro to disallow the copy constructor and operator= functions. +// This should be used in the private: declarations for a class. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ +private: \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + + +// A macro to disallow all the implicit constructors, namely the default +// constructor, copy constructor and operator= functions. This should be +// used in the private: declarations for a class that wants to prevent +// anyone from instantiating it. This is especially useful for classes +// containing only static methods. +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ +private: \ + TypeName(); \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + + +// Macro to disallow allocation in the C++ heap. This should be used +// in the private section for a class. +#define DISALLOW_ALLOCATION() \ +public: \ + void operator delete(void* pointer) { UNREACHABLE(); } \ +private: \ + void* operator new(size_t size); + + +// The USE(x) template is used to silence C++ compiler warnings issued +// for unused variables. +template +static inline void USE(T) { } + + +// On Windows the reentrent version of strtok is called +// strtok_s. Unify on the posix name strtok_r. +#if defined(TARGET_OS_WINDOWS) +#define snprintf _snprintf +#define strtok_r strtok_s +#endif + +#endif // BIN_GLOBALS_H_ diff --git a/runtime/bin/input_stream.dart b/runtime/bin/input_stream.dart new file mode 100644 index 00000000000..b9fb569d655 --- /dev/null +++ b/runtime/bin/input_stream.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/** + * Input is read from a given input stream. Such an input stream can + * be an endpoint, e.g., a socket or a file, or another input stream. + * Multiple input streams can be chained together to operate collaboratively + * on a given input. + */ +interface InputStream { + /** + * Reads [len] bytes into [buffer] buffer starting at [offset] offset. + * [callback] callback is invoked on completion unless it is null. + */ + bool read(List buffer, int offset, int len, void callback()); + + /** + * Reads data from the stream into a buffer until a given [pattern] occurs and + * hands that buffer over as an input to the registered [callback]. + * The callback is not invoked if a read error occurs. + */ + void readUntil(List pattern, void callback(List buffer)); +} + diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc new file mode 100644 index 00000000000..deb644a6d63 --- /dev/null +++ b/runtime/bin/main.cc @@ -0,0 +1,257 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include + +#include "include/dart_api.h" + +#include "bin/builtin.h" +#include "bin/file.h" +#include "bin/globals.h" +#include "bin/process_script.h" + +// snapshot_buffer points to a snapshot if we link in a snapshot otherwise +// it is initialized to NULL. +extern const uint8_t* snapshot_buffer; + + +// Global state that indicates whether pprof symbol information is +// to be generated or not. +static const char* generate_pprof_symbols_filename = NULL; + + +static bool IsValidFlag(const char* name, + const char* prefix, + intptr_t prefix_length) { + intptr_t name_length = strlen(name); + return ((name_length > prefix_length) && + (strncmp(name, prefix, prefix_length) == 0)); +} + + +static const char* ProcessOption(const char* option, const char* name) { + const intptr_t length = strlen(name); + if (strncmp(option, name, length) == 0) { + return (option + length); + } + return NULL; +} + + +static bool ProcessPprofOption(const char* option) { + const char* kProfOption = "--generate_pprof_symbols="; + generate_pprof_symbols_filename = ProcessOption(option, kProfOption); + return generate_pprof_symbols_filename != NULL; +} + + +// Parse out the command line arguments. Returns -1 if the arguments +// are incorrect, 0 otherwise. +static int ParseArguments(int argc, + char** argv, + CommandLineOptions* vm_options, + char** script_name, + CommandLineOptions* dart_options) { + const char* kPrefix = "--"; + const intptr_t kPrefixLen = strlen(kPrefix); + + // Skip the binary name. + int i = 1; + + // Parse out the vm options. + while ((i < argc) && IsValidFlag(argv[i], kPrefix, kPrefixLen)) { + if (ProcessPprofOption(argv[i])) { + i += 1; + Dart_InitPprofSupport(); + continue; + } + vm_options->AddArgument(argv[i]); + i += 1; + } + + // Get the script name. + if (i < argc) { + *script_name = argv[i]; + i += 1; + } else { + return -1; + } + + // Parse out options to be passed to dart main. + while (i < argc) { + dart_options->AddArgument(argv[i]); + i += 1; + } + + return 0; +} + + +static void DumpPprofSymbolInfo() { + if (generate_pprof_symbols_filename != NULL) { + Dart_EnterScope(); + File* pprof_file = File::OpenFile(generate_pprof_symbols_filename, true); + ASSERT(pprof_file != NULL); + void* buffer; + int buffer_size; + Dart_GetPprofSymbolInfo(&buffer, &buffer_size); + if (buffer_size > 0) { + ASSERT(buffer != NULL); + pprof_file->WriteFully(buffer, buffer_size); + } + delete pprof_file; // Closes the file. + Dart_ExitScope(); + } +} + + +static void* MainIsolateInitCallback(void* data) { + const char* script_name = reinterpret_cast(data); + Dart_Result result; + Dart_EnterScope(); + + // Load the specified script. + result = LoadScript(script_name); + if (!Dart_IsValidResult(result)) { + const char* err_msg = Dart_GetErrorCString(result); + fprintf(stderr, "Errors encountered while loading script: %s\n", err_msg); + Dart_ExitScope(); + exit(255); + } + + Dart_Handle library = Dart_GetResult(result); + if (!Dart_IsLibrary(library)) { + fprintf(stderr, + "Expected a library when loading script: %s", + script_name); + Dart_ExitScope(); + exit(255); + } + Builtin_ImportLibrary(library); + // Setup the native resolver for built in library functions. + Builtin_SetNativeResolver(); + + Dart_ExitScope(); + return data; +} + + +static void PrintUsage() { + fprintf(stderr, + "dart [] []\n"); +} + + +static bool HasCompileAll(const CommandLineOptions& options) { + for (int i = 0; i < options.count(); i++) { + if (strcmp(options.GetArgument(i), "--compile_all") == 0) { + return true; + } + } + return false; +} + + +static void PrintObject(FILE* out, Dart_Handle object) { + Dart_Result result = Dart_ObjectToString(object); + if (Dart_IsValidResult(result)) { + Dart_Handle string = Dart_GetResult(result); + PrintString(out, string); + } else { + fprintf(out, "%s\n", Dart_GetErrorCString(result)); + } +} + + +int main(int argc, char** argv) { + char* script_name; + CommandLineOptions vm_options(argc); + CommandLineOptions dart_options(argc); + + // Parse command line arguments. + if (ParseArguments(argc, + argv, + &vm_options, + &script_name, + &dart_options) < 0) { + PrintUsage(); + return 255; + } + + // Initialize the Dart VM. + Dart_Initialize(vm_options.count(), + vm_options.arguments(), + MainIsolateInitCallback); + + // Create an isolate. As a side effect, MainIsolateInitCallback + // gets called, which loads the scripts and libraries. + Dart_Isolate isolate = Dart_CreateIsolate(snapshot_buffer, script_name); + if (isolate == NULL) { + return 255; + } + + Dart_EnterScope(); + // TODO(asiva): Create a dart options object that can be accessed from + // dart code. + if (HasCompileAll(vm_options)) { + Dart_Result result = Dart_CompileAll(); + if (!Dart_IsValidResult(result)) { + fprintf(stderr, "%s\n", Dart_GetErrorCString(result)); + Dart_ExitScope(); + Dart_ShutdownIsolate(); + return 255; // Indicates we encountered an error. + } + } + + // Lookup and invoke the top level main function. + Dart_Handle script_url = Dart_NewString(script_name); + Dart_Result result = Dart_LookupLibrary(script_url); + if (!Dart_IsValidResult(result)) { + fprintf(stderr, "%s\n", Dart_GetErrorCString(result)); + Dart_ExitScope(); + Dart_ShutdownIsolate(); + return 255; // Indicates we encountered an error. + } + Dart_Handle library = Dart_GetResult(result); + result = Dart_InvokeStatic(library, + Dart_NewString(""), + Dart_NewString("main"), + 0, + NULL); + + if (Dart_IsValidResult(result)) { + Dart_Handle result_obj = Dart_GetResult(result); + if (Dart_ExceptionOccurred(result_obj)) { + // Print the exception object. + fprintf(stderr, "An unhandled exception has been thrown\n"); + Dart_Result exception_result = Dart_GetException(result_obj); + assert(Dart_IsValidResult(exception_result)); + PrintObject(stderr, Dart_GetResult(exception_result)); + // Print the stack trace. + Dart_Result stacktrace = Dart_GetStacktrace(result_obj); + assert(Dart_IsValidResult(stacktrace)); + PrintObject(stderr, Dart_GetResult(stacktrace)); + fprintf(stderr, "\n"); + Dart_ExitScope(); + Dart_ShutdownIsolate(); + return 255; // We had an unhandled exception, hence indicate an error. + } + } else { + fprintf(stderr, "%s\n", Dart_GetErrorCString(result)); + Dart_ExitScope(); + Dart_ShutdownIsolate(); + return 255; // Indicates we encountered an error. + } + Dart_ExitScope(); + // Keep handling messages until the last active receive port is closed. + Dart_RunLoop(); + // Dump symbol information for the profiler. + DumpPprofSymbolInfo(); + // Shutdown the isolate. + Dart_ShutdownIsolate(); + return 0; +} diff --git a/runtime/bin/output_stream.dart b/runtime/bin/output_stream.dart new file mode 100644 index 00000000000..edcec3ded6b --- /dev/null +++ b/runtime/bin/output_stream.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/** + * Output is written to a given output stream. Such an output stream can + * be an endpoint, e.g., a socket or a file, or another output stream. + * Multiple output streams can be chained together to operate collaboratively + * on a given output. + */ +interface OutputStream { + /** + * Writes [len] bytes into [buffer] buffer starting at [offset] offset]. + * If write succeedes true is returned. Otherwise false is returned + * and [callback] callback is invoked on completion. + */ + bool write(List buffer, int offset, int len, void callback()); +} + diff --git a/runtime/bin/process.cc b/runtime/bin/process.cc new file mode 100644 index 00000000000..6ae139972ca --- /dev/null +++ b/runtime/bin/process.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "bin/dartutils.h" +#include "bin/process.h" + +#include "include/dart_api.h" + +void FUNCTION_NAME(Process_Start)(Dart_NativeArguments args) { + Dart_EnterScope(); + Dart_Handle process = Dart_GetNativeArgument(args, 0); + intptr_t in; + intptr_t out; + intptr_t err; + intptr_t exit_event; + const char* path = + DartUtils::GetStringValue(Dart_GetNativeArgument(args, 1)); + Dart_Handle arguments = Dart_GetNativeArgument(args, 2); + ASSERT(Dart_IsArray(arguments)); + Dart_Result result = Dart_GetLength(arguments); + ASSERT(Dart_IsValidResult(result)); + intptr_t length = Dart_GetResultAsCIntptr(result); + char** string_args = new char*[length]; + for (int i = 0; i < length; i++) { + result = Dart_ArrayGetAt(arguments, i); + ASSERT(Dart_IsValidResult(result)); + Dart_Handle arg = Dart_GetResult(result); + string_args[i] = const_cast(DartUtils::GetStringValue(arg)); + } + Dart_Handle in_handle = Dart_GetNativeArgument(args, 3); + Dart_Handle out_handle = Dart_GetNativeArgument(args, 4); + Dart_Handle err_handle = Dart_GetNativeArgument(args, 5); + Dart_Handle exit_handle = Dart_GetNativeArgument(args, 6); + Dart_Handle status_handle = Dart_GetNativeArgument(args, 7); + intptr_t pid = -1; + static const int kMaxChildOsErrorMessageLength = 256; + char os_error_message[kMaxChildOsErrorMessageLength]; + + int error_code = Process::Start( + path, string_args, length, + &in, &out, &err, &pid, &exit_event, + os_error_message, kMaxChildOsErrorMessageLength); + if (error_code == 0) { + DartUtils::SetIntegerInstanceField(in_handle, DartUtils::kIdFieldName, in); + DartUtils::SetIntegerInstanceField( + out_handle, DartUtils::kIdFieldName, out); + DartUtils::SetIntegerInstanceField( + err_handle, DartUtils::kIdFieldName, err); + DartUtils::SetIntegerInstanceField( + exit_handle, DartUtils::kIdFieldName, exit_event); + DartUtils::SetIntegerInstanceField(process, "_pid", pid); + } else { + DartUtils::SetIntegerInstanceField( + status_handle, "_errorCode", error_code); + DartUtils::SetStringInstanceField( + status_handle, "_errorMessage", os_error_message); + } + delete[] string_args; + Dart_SetReturnValue(args, Dart_NewBoolean(error_code == 0)); + Dart_ExitScope(); +} + +void FUNCTION_NAME(Process_Kill)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t pid = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1)); + bool success = Process::Kill(pid); + Dart_SetReturnValue(args, Dart_NewBoolean(success)); + Dart_ExitScope(); +} diff --git a/runtime/bin/process.dart b/runtime/bin/process.dart new file mode 100644 index 00000000000..d96b6464ca0 --- /dev/null +++ b/runtime/bin/process.dart @@ -0,0 +1,66 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +interface Process factory _Process { + /* + * Creates a new process object preparing to run the executable + * found at [path] with the specified [arguments]. [arguments] has + * to be a const string array, c.f. bug 5314640. + */ + Process(String path, List arguments); + + /* + * Start the process by running the specified executable. An + * exception of type [ProcessException] is thrown if the process + * cannot be started. There is a remote possibility of an exception + * being thrown even though the child process did actually start. + */ + void start(); + + /* + * Returns an input stream of the process stdout. + */ + InputStream get stdoutStream(); + + /* + * Returns an input stream of the process stderr. + */ + InputStream get stderrStream(); + + /* + * Returns an output stream to the process stdin. + */ + OutputStream get stdinStream(); + + /* + * Sets an exit handler which gets invoked when the process terminates. + */ + void setExitHandler(void callback(int exitCode)); + + /* + * Kills the process with [signal]. + */ + bool kill(); + + /* + * Terminates the streams and closes the exit handler of a process. + */ + void close(); +} + + +class ProcessException implements Exception { + const ProcessException([String this.message, int this.errorCode = 0]); + String toString() => "ProcessException: $message"; + + /* + * Contains the system message for the process exception if any. + */ + final String message; + + /* + * Contains the OS error code for the process exception if any. + */ + final int errorCode; +} diff --git a/runtime/bin/process.h b/runtime/bin/process.h new file mode 100644 index 00000000000..04a513c7ee1 --- /dev/null +++ b/runtime/bin/process.h @@ -0,0 +1,31 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_PROCESS_H_ +#define BIN_PROCESS_H_ + +#include "bin/builtin.h" +#include "bin/globals.h" + + +class Process { + public: + static int Start(const char* path, + char* arguments[], + intptr_t arguments_length, + intptr_t* in, + intptr_t* out, + intptr_t* err, + intptr_t* id, + intptr_t* exit_handler, + char* os_error_message, + int os_error_message_len); + + static bool Kill(intptr_t id); + + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(Process); +}; + +#endif // BIN_PROCESS_H_ diff --git a/runtime/bin/process_impl.dart b/runtime/bin/process_impl.dart new file mode 100644 index 00000000000..0e0b6957399 --- /dev/null +++ b/runtime/bin/process_impl.dart @@ -0,0 +1,153 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class _ProcessStartStatus { + int _errorCode; // Set to OS error code if process start failed. + String _errorMessage; // Set to OS error message if process start failed. +} + + +class _Process implements Process { + + _Process(String path, List arguments) { + _path = path; + { + int len = arguments.length; + _arguments = new ObjectArray(len); + for (int i = 0; i < len; i++) { + _arguments[i] = arguments[i]; + } + } + _in = new _Socket._internal(); + _out = new _Socket._internal(); + _err = new _Socket._internal(); + _exitHandler = new _Socket._internal(); + _closed = false; + _killed = false; + _started = false; + _exitHandlerCallback = null; + } + + void start() { + var status = new _ProcessStartStatus(); + bool success = _start( + _path, _arguments, _in, _out, _err, _exitHandler, status); + if (!success) { + close(); + throw new ProcessException(status._errorMessage, status._errorCode); + } + _started = true; + if (_exitHandlerCallback !== null) { + setExitHandler(_exitHandlerCallback); + } + } + + bool _start(String path, + List arguments, + Socket input, + Socket output, + Socket error, + Socket exitHandler, + _ProcessStartStatus status) native "Process_Start"; + + InputStream get stdoutStream() { + if (_closed) { + throw new ProcessException("Process closed"); + } + return _in.inputStream; + } + + InputStream get stderrStream() { + if (_closed) { + throw new ProcessException("Process closed"); + } + return _err.inputStream; + } + + OutputStream get stdinStream() { + if (_closed) { + throw new ProcessException("Process closed"); + } + return _out.outputStream; + } + + bool kill() { + if (_closed && _pid == null) { + throw new ProcessException("Process closed"); + } + if (_killed) { + return true; + } + if (_kill(_pid)) { + _killed = true; + return true; + } + return false; + } + + void _kill(int pid) native "Process_Kill"; + + void close() { + if (_closed) { + throw new ProcessException("Process closed"); + } + _in.close(); + _out.close(); + _err.close(); + _exitHandler.close(); + _closed = true; + } + + void setExitHandler(void callback(int exitCode)) { + if (_closed) { + throw new ProcessException("Process closed"); + } + if (_killed) { + throw new ProcessException("Process killed"); + } + if (_started) { + _exitHandler.setDataHandler(() { + List buffer = new List(4); + SocketInputStream input = _exitHandler.inputStream; + + int getExitValue(List ints) { + return ints[0] + (ints[1] << 8) + (ints[2] << 16) + (ints[3] << 24); + } + + void readData() { + callback(getExitValue(buffer)); + } + + bool result = input.read(buffer, 0, 4, readData); + if (result) { + callback(getExitValue(buffer)); + } + }); + } else { + _exitHandlerCallback = callback; + } + } + + String _path; + + ObjectArray _arguments; + + Socket _in; + + Socket _out; + + Socket _err; + + Socket _exitHandler; + + int _pid; + + bool _closed; + + bool _killed; + + bool _started; + + var _exitHandlerCallback; +} diff --git a/runtime/bin/process_linux.cc b/runtime/bin/process_linux.cc new file mode 100644 index 00000000000..e43f486f761 --- /dev/null +++ b/runtime/bin/process_linux.cc @@ -0,0 +1,302 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include + +#include "bin/fdutils.h" +#include "bin/process.h" +#include "bin/set.h" + + +class ActiveProcess { + public: + pid_t pid; + intptr_t fd; + + bool operator==(const ActiveProcess &other) const { + if (pid == other.pid) { + return true; + } + return false; + } +}; + + +static Set activeProcesses; + + +static char* SafeStrNCpy(char* dest, const char* src, size_t n) { + strncpy(dest, src, n); + dest[n - 1] = '\0'; + return dest; +} + + +static void SetChildOsErrorMessage(char* os_error_message, + int os_error_message_len) { + SafeStrNCpy(os_error_message, strerror(errno), os_error_message_len); +} + + +void ExitHandle(int processSignal, siginfo_t* siginfo, void* tmp) { + assert(processSignal == SIGCHLD); + struct sigaction act; + bzero(&act, sizeof(act)); + act.sa_handler = SIG_IGN; + act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; + if (sigaction(SIGCHLD, &act, 0) != 0) { + perror("Process start: disabling signal handler failed"); + } + pid_t pid = siginfo->si_pid; + ActiveProcess element; + element.pid = pid; + ActiveProcess* current = activeProcesses.Remove(element); + if (current != NULL) { + intptr_t message = siginfo->si_status; + intptr_t result = + FDUtils::WriteToBlocking(current->fd, &message, sizeof(message)); + if (result != sizeof(message)) { + perror("ExitHandle notification failed"); + } + close(current->fd); + + delete current; + } + act.sa_handler = 0; + act.sa_sigaction = ExitHandle; + if (sigaction(SIGCHLD, &act, 0) != 0) { + perror("Process start: enabling signal handler failed"); + } +} + + +int Process::Start(const char* path, + char* arguments[], + intptr_t arguments_length, + intptr_t* in, + intptr_t* out, + intptr_t* err, + intptr_t* id, + intptr_t* exit_event, + char* os_error_message, + int os_error_message_len) { + pid_t pid; + int read_in[2]; // Pipe for stdout to child process. + int read_err[2]; // Pipe for stderr to child process. + int write_out[2]; // Pipe for stdin to child process. + int exec_control[2]; // Pipe to get the result from exec. + int result; + + result = pipe(read_in); + if (result < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); + return errno; + } + + result = pipe(read_err); + if (result < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + close(read_in[0]); + close(read_in[1]); + fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); + return errno; + } + + result = pipe(write_out); + if (result < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + close(read_in[0]); + close(read_in[1]); + close(read_err[0]); + close(read_err[1]); + fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); + return errno; + } + + result = pipe(exec_control); + if (result < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + close(read_in[0]); + close(read_in[1]); + close(read_err[0]); + close(read_err[1]); + close(write_out[0]); + close(write_out[1]); + fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); + return errno; + } + + // Set close on exec on the write file descriptor of the exec control pipe. + result = fcntl( + exec_control[1], F_SETFD, fcntl(exec_control[1], F_GETFD) | FD_CLOEXEC); + if (result < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + close(read_in[0]); + close(read_in[1]); + close(read_err[0]); + close(read_err[1]); + close(write_out[0]); + close(write_out[1]); + close(exec_control[0]); + close(exec_control[1]); + fprintf(stderr, "fcntl failed: %s\n", os_error_message); + return errno; + } + + char* program_arguments[arguments_length + 2]; + program_arguments[0] = const_cast(path); + for (int i = 0; i < arguments_length; i++) { + program_arguments[i + 1] = arguments[i]; + } + program_arguments[arguments_length + 1] = NULL; + + struct sigaction act; + bzero(&act, sizeof(act)); + act.sa_sigaction = ExitHandle; + act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; + if (sigaction(SIGCHLD, &act, 0) != 0) { + perror("Process start: setting signal handler failed"); + } + pid = fork(); + if (pid < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + close(read_in[0]); + close(read_in[1]); + close(read_err[0]); + close(read_err[1]); + close(write_out[0]); + close(write_out[1]); + close(exec_control[0]); + close(exec_control[1]); + return errno; + } else if (pid == 0) { + // Wait for parent process before setting up the child process. + char msg; + int bytes_read = FDUtils::ReadFromBlocking(read_in[0], &msg, sizeof(msg)); + if (bytes_read != sizeof(msg)) { + perror("Failed receiving notification message"); + exit(1); + } + + close(write_out[1]); + close(read_in[0]); + close(read_err[0]); + close(exec_control[0]); + + dup2(write_out[0], STDIN_FILENO); + close(write_out[0]); + + dup2(read_in[1], STDOUT_FILENO); + close(read_in[1]); + + dup2(read_err[1], STDERR_FILENO); + close(read_err[1]); + + execvp(path, const_cast(program_arguments)); + // In the case of failure write the errno and the OS error message + // to the exec control pipe. + int child_errno = errno; + char* os_error_message = strerror(errno); + ASSERT(sizeof(child_errno) == sizeof(errno)); + int bytes_written = + FDUtils::WriteToBlocking( + exec_control[1], &child_errno, sizeof(child_errno)); + if (bytes_written == sizeof(child_errno)) { + FDUtils::WriteToBlocking( + exec_control[1], os_error_message, strlen(os_error_message) + 1); + } + close(exec_control[1]); + exit(1); + } + + int event_fds[2]; + result = pipe(event_fds); + if (result < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + close(read_in[0]); + close(read_in[1]); + close(read_err[0]); + close(read_err[1]); + close(write_out[0]); + close(write_out[1]); + fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); + return errno; + } + + ActiveProcess* activeProcess = new ActiveProcess(); + activeProcess->pid = pid; + activeProcess->fd = event_fds[1]; + activeProcesses.Add(*activeProcess); + *exit_event = event_fds[0]; + FDUtils::SetNonBlocking(event_fds[0]); + + // Notify child process to start. + char msg = '1'; + result = FDUtils::WriteToBlocking(read_in[1], &msg, sizeof(msg)); + if (result != sizeof(msg)) { + perror("Failed sending notification message"); + } + + // Read exec result from child. If no data is returned the exec was + // successful and the exec call closed the pipe. Otherwise the errno + // is written to the pipe. + close(exec_control[1]); + int child_errno; + int bytes_read = -1; + ASSERT(sizeof(child_errno) == sizeof(errno)); + bytes_read = + FDUtils::ReadFromBlocking( + exec_control[0], &child_errno, sizeof(child_errno)); + if (bytes_read == sizeof(child_errno)) { + bytes_read = FDUtils::ReadFromBlocking(exec_control[0], + os_error_message, + os_error_message_len); + os_error_message[os_error_message_len - 1] = '\0'; + } + close(exec_control[0]); + + // Return error code if any failures. + if (bytes_read != 0) { + close(read_in[0]); + close(read_in[1]); + close(read_err[0]); + close(read_err[1]); + close(write_out[0]); + close(write_out[1]); + if (bytes_read == -1) { + return errno; // Read failed. + } else { + return child_errno; // Exec failed. + } + } + + FDUtils::SetNonBlocking(read_in[0]); + *in = read_in[0]; + close(read_in[1]); + FDUtils::SetNonBlocking(write_out[1]); + *out = write_out[1]; + close(write_out[0]); + FDUtils::SetNonBlocking(read_err[0]); + *err = read_err[0]; + close(read_err[1]); + + *id = pid; + return 0; +} + + +bool Process::Kill(intptr_t id) { + int result = kill(id, SIGKILL); + if (result == -1) { + return false; + } + return true; +} diff --git a/runtime/bin/process_macos.cc b/runtime/bin/process_macos.cc new file mode 100644 index 00000000000..e43f486f761 --- /dev/null +++ b/runtime/bin/process_macos.cc @@ -0,0 +1,302 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include + +#include "bin/fdutils.h" +#include "bin/process.h" +#include "bin/set.h" + + +class ActiveProcess { + public: + pid_t pid; + intptr_t fd; + + bool operator==(const ActiveProcess &other) const { + if (pid == other.pid) { + return true; + } + return false; + } +}; + + +static Set activeProcesses; + + +static char* SafeStrNCpy(char* dest, const char* src, size_t n) { + strncpy(dest, src, n); + dest[n - 1] = '\0'; + return dest; +} + + +static void SetChildOsErrorMessage(char* os_error_message, + int os_error_message_len) { + SafeStrNCpy(os_error_message, strerror(errno), os_error_message_len); +} + + +void ExitHandle(int processSignal, siginfo_t* siginfo, void* tmp) { + assert(processSignal == SIGCHLD); + struct sigaction act; + bzero(&act, sizeof(act)); + act.sa_handler = SIG_IGN; + act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; + if (sigaction(SIGCHLD, &act, 0) != 0) { + perror("Process start: disabling signal handler failed"); + } + pid_t pid = siginfo->si_pid; + ActiveProcess element; + element.pid = pid; + ActiveProcess* current = activeProcesses.Remove(element); + if (current != NULL) { + intptr_t message = siginfo->si_status; + intptr_t result = + FDUtils::WriteToBlocking(current->fd, &message, sizeof(message)); + if (result != sizeof(message)) { + perror("ExitHandle notification failed"); + } + close(current->fd); + + delete current; + } + act.sa_handler = 0; + act.sa_sigaction = ExitHandle; + if (sigaction(SIGCHLD, &act, 0) != 0) { + perror("Process start: enabling signal handler failed"); + } +} + + +int Process::Start(const char* path, + char* arguments[], + intptr_t arguments_length, + intptr_t* in, + intptr_t* out, + intptr_t* err, + intptr_t* id, + intptr_t* exit_event, + char* os_error_message, + int os_error_message_len) { + pid_t pid; + int read_in[2]; // Pipe for stdout to child process. + int read_err[2]; // Pipe for stderr to child process. + int write_out[2]; // Pipe for stdin to child process. + int exec_control[2]; // Pipe to get the result from exec. + int result; + + result = pipe(read_in); + if (result < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); + return errno; + } + + result = pipe(read_err); + if (result < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + close(read_in[0]); + close(read_in[1]); + fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); + return errno; + } + + result = pipe(write_out); + if (result < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + close(read_in[0]); + close(read_in[1]); + close(read_err[0]); + close(read_err[1]); + fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); + return errno; + } + + result = pipe(exec_control); + if (result < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + close(read_in[0]); + close(read_in[1]); + close(read_err[0]); + close(read_err[1]); + close(write_out[0]); + close(write_out[1]); + fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); + return errno; + } + + // Set close on exec on the write file descriptor of the exec control pipe. + result = fcntl( + exec_control[1], F_SETFD, fcntl(exec_control[1], F_GETFD) | FD_CLOEXEC); + if (result < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + close(read_in[0]); + close(read_in[1]); + close(read_err[0]); + close(read_err[1]); + close(write_out[0]); + close(write_out[1]); + close(exec_control[0]); + close(exec_control[1]); + fprintf(stderr, "fcntl failed: %s\n", os_error_message); + return errno; + } + + char* program_arguments[arguments_length + 2]; + program_arguments[0] = const_cast(path); + for (int i = 0; i < arguments_length; i++) { + program_arguments[i + 1] = arguments[i]; + } + program_arguments[arguments_length + 1] = NULL; + + struct sigaction act; + bzero(&act, sizeof(act)); + act.sa_sigaction = ExitHandle; + act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; + if (sigaction(SIGCHLD, &act, 0) != 0) { + perror("Process start: setting signal handler failed"); + } + pid = fork(); + if (pid < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + close(read_in[0]); + close(read_in[1]); + close(read_err[0]); + close(read_err[1]); + close(write_out[0]); + close(write_out[1]); + close(exec_control[0]); + close(exec_control[1]); + return errno; + } else if (pid == 0) { + // Wait for parent process before setting up the child process. + char msg; + int bytes_read = FDUtils::ReadFromBlocking(read_in[0], &msg, sizeof(msg)); + if (bytes_read != sizeof(msg)) { + perror("Failed receiving notification message"); + exit(1); + } + + close(write_out[1]); + close(read_in[0]); + close(read_err[0]); + close(exec_control[0]); + + dup2(write_out[0], STDIN_FILENO); + close(write_out[0]); + + dup2(read_in[1], STDOUT_FILENO); + close(read_in[1]); + + dup2(read_err[1], STDERR_FILENO); + close(read_err[1]); + + execvp(path, const_cast(program_arguments)); + // In the case of failure write the errno and the OS error message + // to the exec control pipe. + int child_errno = errno; + char* os_error_message = strerror(errno); + ASSERT(sizeof(child_errno) == sizeof(errno)); + int bytes_written = + FDUtils::WriteToBlocking( + exec_control[1], &child_errno, sizeof(child_errno)); + if (bytes_written == sizeof(child_errno)) { + FDUtils::WriteToBlocking( + exec_control[1], os_error_message, strlen(os_error_message) + 1); + } + close(exec_control[1]); + exit(1); + } + + int event_fds[2]; + result = pipe(event_fds); + if (result < 0) { + SetChildOsErrorMessage(os_error_message, os_error_message_len); + close(read_in[0]); + close(read_in[1]); + close(read_err[0]); + close(read_err[1]); + close(write_out[0]); + close(write_out[1]); + fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); + return errno; + } + + ActiveProcess* activeProcess = new ActiveProcess(); + activeProcess->pid = pid; + activeProcess->fd = event_fds[1]; + activeProcesses.Add(*activeProcess); + *exit_event = event_fds[0]; + FDUtils::SetNonBlocking(event_fds[0]); + + // Notify child process to start. + char msg = '1'; + result = FDUtils::WriteToBlocking(read_in[1], &msg, sizeof(msg)); + if (result != sizeof(msg)) { + perror("Failed sending notification message"); + } + + // Read exec result from child. If no data is returned the exec was + // successful and the exec call closed the pipe. Otherwise the errno + // is written to the pipe. + close(exec_control[1]); + int child_errno; + int bytes_read = -1; + ASSERT(sizeof(child_errno) == sizeof(errno)); + bytes_read = + FDUtils::ReadFromBlocking( + exec_control[0], &child_errno, sizeof(child_errno)); + if (bytes_read == sizeof(child_errno)) { + bytes_read = FDUtils::ReadFromBlocking(exec_control[0], + os_error_message, + os_error_message_len); + os_error_message[os_error_message_len - 1] = '\0'; + } + close(exec_control[0]); + + // Return error code if any failures. + if (bytes_read != 0) { + close(read_in[0]); + close(read_in[1]); + close(read_err[0]); + close(read_err[1]); + close(write_out[0]); + close(write_out[1]); + if (bytes_read == -1) { + return errno; // Read failed. + } else { + return child_errno; // Exec failed. + } + } + + FDUtils::SetNonBlocking(read_in[0]); + *in = read_in[0]; + close(read_in[1]); + FDUtils::SetNonBlocking(write_out[1]); + *out = write_out[1]; + close(write_out[0]); + FDUtils::SetNonBlocking(read_err[0]); + *err = read_err[0]; + close(read_err[1]); + + *id = pid; + return 0; +} + + +bool Process::Kill(intptr_t id) { + int result = kill(id, SIGKILL); + if (result == -1) { + return false; + } + return true; +} diff --git a/runtime/bin/process_script.cc b/runtime/bin/process_script.cc new file mode 100644 index 00000000000..a96de664845 --- /dev/null +++ b/runtime/bin/process_script.cc @@ -0,0 +1,158 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Handle dart scripts. + +#include +#include +#include +#include + +#include "include/dart_api.h" + +#include "bin/builtin.h" +#include "bin/file.h" +#include "bin/globals.h" +#include "bin/process_script.h" + +static const char* CanonicalizeUrl(const char* reference_dir, + const char* filename) { + static const char* kDartScheme = "dart:"; + static const intptr_t kDartSchemeLen = strlen(kDartScheme); + // If the URL starts with "dart:" then it is not modified as it will be + // handled by the VM internally. + if (strncmp(filename, kDartScheme, kDartSchemeLen) == 0) { + return strdup(filename); + } + + if (File::IsAbsolutePath(filename)) { + return strdup(filename); + } + + char* path = strdup(reference_dir); + if (path == NULL) { + return NULL; + } + char* path_sep = strrchr(path, File::PathSeparator()[0]); + if (path_sep == NULL) { + // No separator found: Reference is a file in local directory. + return strdup(filename); + } + *path_sep = '\0'; + intptr_t len = snprintf(NULL, 0, "%s%s%s", + path, File::PathSeparator(), filename); + char* absolute_filename = reinterpret_cast(malloc(len + 1)); + ASSERT(absolute_filename != NULL); + + snprintf(absolute_filename, len + 1, "%s%s%s", + path, File::PathSeparator(), filename); + + free(path); + return absolute_filename; +} + + +static Dart_Result ReadStringFromFile(const char* filename) { + File* file = File::OpenFile(filename, false); + if (file == NULL) { + const char* format = "Unable to open file: %s"; + intptr_t len = snprintf(NULL, 0, format, filename); + // TODO(iposva): Allocate from the zone instead of leaking error string + // here. On the other hand the binary is about the exit anyway. + char* error_msg = reinterpret_cast(malloc(len + 1)); + snprintf(error_msg, len + 1, format, filename); + return Dart_ErrorResult(error_msg); + } + intptr_t len = file->Length(); + char* text_buffer = reinterpret_cast(malloc(len + 1)); + if (text_buffer == NULL) { + delete file; + return Dart_ErrorResult("Unable to allocate buffer"); + } + if (!file->ReadFully(text_buffer, len)) { + delete file; + return Dart_ErrorResult("Unable to fully read contents"); + } + text_buffer[len] = '\0'; + delete file; + Dart_Handle str = Dart_NewString(text_buffer); + free(text_buffer); + return Dart_ResultAsObject(str); +} + + +static Dart_Result LibraryTagHandler(Dart_LibraryTag tag, + Dart_Handle library, + Dart_Handle url) { + if (!Dart_IsLibrary(library)) { + return Dart_ErrorResult("not a library"); + } + if (!Dart_IsString8(url)) { + return Dart_ErrorResult("url is not a string"); + } + Dart_Result result = Dart_StringToCString(url); + if (!Dart_IsValidResult(result)) { + return Dart_ErrorResult("accessing url characters failed"); + } + const char* url_chars = Dart_GetResultAsCString(result); + + if (tag == kCanonicalizeUrl) { + // Create the full path based on the including library and the current url. + + // Get the url of the calling library. + result = Dart_LibraryUrl(library); + if (!Dart_IsValidResult(result)) { + return Dart_ErrorResult("accessing library url failed"); + } + Dart_Handle library_url = Dart_GetResult(result); + if (!Dart_IsString8(library_url)) { + return Dart_ErrorResult("library url is not a string"); + } + result = Dart_StringToCString(library_url); + if (!Dart_IsValidResult(result)) { + return Dart_ErrorResult("accessing library url characters failed"); + } + const char* library_url_chars = Dart_GetResultAsCString(result); + + // Calculate the path. + const char* canon_url_chars = CanonicalizeUrl(library_url_chars, url_chars); + Dart_Handle canon_url = Dart_NewString(canon_url_chars); + free(const_cast(canon_url_chars)); + + return Dart_ResultAsObject(canon_url); + } + + // The tag is either an import or a source tag. Read the file based on the + // url chars. + result = ReadStringFromFile(url_chars); + if (!Dart_IsValidResult(result)) { + return result; + } + Dart_Handle source = Dart_GetResult(result); + + if (tag == kImportTag) { + result = Dart_LoadLibrary(url, source); + if (Dart_IsValidResult(result)) { + // TODO(iposva): Should the builtin library be added to all libraries? + Dart_Handle new_lib = Dart_GetResult(result); + Builtin_ImportLibrary(new_lib); + } + return result; + } else if (tag == kSourceTag) { + return Dart_LoadSource(library, url, source); + } + return Dart_ErrorResult("wrong tag"); +} + + +Dart_Result LoadScript(const char* script_name) { + Dart_Result result = ReadStringFromFile(script_name); + if (!Dart_IsValidResult(result)) { + return result; + } + Dart_Handle source = Dart_GetResult(result); + Dart_Handle url = Dart_NewString(script_name); + + return Dart_LoadScript(url, source, LibraryTagHandler); +} diff --git a/runtime/bin/process_script.h b/runtime/bin/process_script.h new file mode 100644 index 00000000000..820be812157 --- /dev/null +++ b/runtime/bin/process_script.h @@ -0,0 +1,60 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_PROCESS_SCRIPT_H_ +#define BIN_PROCESS_SCRIPT_H_ + +#include +#include +#include +#include + +class CommandLineOptions { + public: + explicit CommandLineOptions(int max_count) + : count_(0), max_count_(max_count), arguments_(NULL) { + static const int kWordSize = sizeof(intptr_t); + arguments_ = reinterpret_cast(malloc(max_count * kWordSize)); + if (arguments_ == NULL) { + max_count_ = 0; + } + } + ~CommandLineOptions() { + free(arguments_); + count_ = 0; + max_count_ = 0; + arguments_ = NULL; + } + + int count() const { return count_; } + char** arguments() const { return arguments_; } + + char* GetArgument(int index) const { + return (index >= 0 && index < count_) ? arguments_[index] : NULL; + } + void AddArgument(char* argument) { + if (count_ < max_count_) { + arguments_[count_] = argument; + count_ += 1; + } else { + abort(); // We should never get into this situation. + } + } + + void operator delete(void* pointer) { abort(); } + + private: + void* operator new(size_t size); + CommandLineOptions(const CommandLineOptions&); + void operator=(const CommandLineOptions&); + + int count_; + int max_count_; + char** arguments_; +}; + + +extern Dart_Result LoadScript(const char* script_name); + +#endif // BIN_PROCESS_SCRIPT_H_ diff --git a/runtime/bin/process_test.cc b/runtime/bin/process_test.cc new file mode 100644 index 00000000000..e9b09b20ce8 --- /dev/null +++ b/runtime/bin/process_test.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include + +/* + * Run ./process_test + * : 0 = stdout, 1 = stderr, 2 = stdout and stderr + * : program terminates after replies + * : program terminates with exit code + * : 0 = program terminates regularly, 1 = program segfaults + */ +int main(int argc, char* argv[]) { + if (argc != 5) { + fprintf(stderr, + "./process_test \n"); + exit(1); + } + + int outstream = atoi(argv[1]); + if (outstream < 0 || outstream > 2) { + fprintf(stderr, "unknown outstream"); + exit(1); + } + + int echo_counter = 0; + int echo_count = atoi(argv[2]); + int exit_code = atoi(argv[3]); + int crash = atoi(argv[4]); + + const int kLineSize = 128; + char line[kLineSize]; + + while ((echo_count != echo_counter) && + (fgets(line, kLineSize, stdin) != NULL)) { + if (outstream == 0) { + fprintf(stdout, "%s", line); + } else if (outstream == 1) { + fprintf(stderr, "%s", line); + } else if (outstream == 2) { + fprintf(stdout, "%s", line); + fprintf(stderr, "%s", line); + } + echo_counter++; + } + + if (crash == 1) { + int* segfault = NULL; + *segfault = 1; + } + + + return exit_code; +} diff --git a/runtime/bin/process_win.cc b/runtime/bin/process_win.cc new file mode 100644 index 00000000000..29c0a0281fc --- /dev/null +++ b/runtime/bin/process_win.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "bin/builtin.h" +#include "bin/globals.h" +#include "bin/process.h" + +int Process::Start(const char* path, + char* arguments[], + intptr_t arguments_length, + intptr_t* in, + intptr_t* out, + intptr_t* err, + intptr_t* id, + intptr_t* exit_event, + char* os_error_message, + int os_error_message_len) { + // Setup info structures. + STARTUPINFO startup_info; + PROCESS_INFORMATION process_info; + ZeroMemory(&startup_info, sizeof(startup_info)); + startup_info.cb = sizeof(startup_info); + ZeroMemory(&process_info, sizeof(process_info)); + + // TODO(ager): Once sockets are implemented, use the supplied + // arguments as in, out and err in the startup info. + + // Compute command-line length. + int command_line_length = strlen(path); + for (int i = 0; i < arguments_length; i++) { + command_line_length += strlen(arguments[i]); + } + // Account for two occurrences of '"' around the command, one + // space per argument and a terminating '\0'. + command_line_length += 2 + arguments_length + 1; + static const int kMaxCommandLineLength = 32768; + if (command_line_length > kMaxCommandLineLength) { + return 1; + } + + // Put together command-line string. + char* command_line = new char[command_line_length]; + int len = 0; + int remaining = command_line_length; + int written = snprintf(command_line + len, remaining, "\"%s\"", path); + len += written; + remaining -= written; + ASSERT(remaining >= 0); + for (int i = 0; i < arguments_length; i++) { + written = snprintf(command_line + len, remaining, " %s", arguments[i]); + len += written; + remaining -= written; + ASSERT(remaining >= 0); + } + + // Create process. + BOOL result = CreateProcess(NULL, // ApplicationName + command_line, + NULL, // ProcessAttributes + NULL, // ThreadAttributes + FALSE, // InheritHandles + 0, // CreationFlags + NULL, // Environment + NULL, // CurrentDirectory, + &startup_info, + &process_info); + + // Deallocate command-line string. + delete[] command_line; + + if (result == 0) { + return 1; + } + + // Return process handle. + *id = reinterpret_cast(process_info.hProcess); + return 0; +} + +bool Process::Kill(intptr_t id) { + HANDLE process_handle = reinterpret_cast(id); + BOOL result = TerminateProcess(process_handle, -1); + if (result == 0) { + return false; + } + CloseHandle(process_handle); + return true; +} diff --git a/runtime/bin/run_vm_tests.cc b/runtime/bin/run_vm_tests.cc new file mode 100644 index 00000000000..b90b34f32d2 --- /dev/null +++ b/runtime/bin/run_vm_tests.cc @@ -0,0 +1,98 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include + +#include "vm/dart.h" +#include "vm/unit_test.h" + +// TODO(iposva, asiva): This is a placeholder for the real unittest framework. +namespace dart { + +// Only run tests that match the filter string. The default does not match any +// tests. +static const char* const kNoTests = "No Test"; +static const char* const kAllTests = "All Tests"; +static const char* const kListTests = "List Tests"; +static const char* test_filter = kNoTests; + +static int test_matches = 0; + + +void TestCase::Run() { + fprintf(stdout, "Running test: %s\n", name()); + (*run_)(); + fprintf(stdout, "Done: %s\n", name()); +} + + +void TestCaseBase::RunTest() { + if ((test_filter == kAllTests) || (strcmp(test_filter, this->name()) == 0)) { + this->Run(); + test_matches++; + } else if (test_filter == kListTests) { + fprintf(stdout, "%s\n", this->name()); + test_matches++; + } +} + + +static void PrintUsage() { + fprintf(stderr, "run_vm_tests [--list | --all | ]\n"); + fprintf(stderr, "run_vm_tests [vm-flags ...]\n"); +} + + +static void* TestIsolateInitCallback(void* data) { + ASSERT(data == NULL); + return reinterpret_cast(0); +} + + +static int Main(int argc, char** argv) { + // Flags being passed to the Dart VM. + int dart_argc = 0; + char** dart_argv = NULL; + + if (argc < 2) { + // Bad parameter count. + PrintUsage(); + return 1; + } else if (argc == 2) { + if (strcmp(argv[1], "--list") == 0) { + test_filter = kListTests; + // List all the tests and exit without initializing the VM at all. + TestCaseBase::RunAll(); + return 0; + } else if (strcmp(argv[1], "--all") == 0) { + test_filter = kAllTests; + } else { + test_filter = argv[1]; + } + } else { + // First argument is the test name, the rest are vm flags. + test_filter = argv[1]; + // Remove the first two values from the arguments. + dart_argc = argc - 2; + dart_argv = &argv[2]; + } + bool init_success = Dart::InitOnce(dart_argc, dart_argv, + TestIsolateInitCallback); + ASSERT(init_success); + // Apply the test filter to all registered tests. + TestCaseBase::RunAll(); + // Print a warning message if no tests were matched. + if (test_matches == 0) { + fprintf(stderr, "No tests matched: %s\n", test_filter); + return 1; + } + return 0; +} + +} // namespace dart + + +int main(int argc, char** argv) { + return dart::Main(argc, argv); +} diff --git a/runtime/bin/set.h b/runtime/bin/set.h new file mode 100644 index 00000000000..e4c50969896 --- /dev/null +++ b/runtime/bin/set.h @@ -0,0 +1,128 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_SET_H_ +#define BIN_SET_H_ + +#include + +/* + * Set implements a collection of distinct objects. + */ +template +class Set { + private: + struct Node { + T object_; + Node* next_; + }; + + public: + class Iterator { + public: + explicit Iterator(Set* list) : list_(list) { + if (list != NULL) { + next_ = list->head_; + } else { + next_ = NULL; + } + } + + bool HasNext() const { + return next_ != NULL; + } + + void GetNext(T* entry) { + *entry = next_->object_; + next_ = next_->next_; + } + + private: + const Set* list_; + struct Node* next_; + }; + + Set() { + head_ = NULL; + tail_ = NULL; + size_ = 0; + } + + ~Set() {} + + bool Add(const T& element) { + Node* new_node = new Node; + new_node->object_ = element; + new_node->next_ = NULL; + + if (Contains(element)) { + return false; + } + + if (IsEmpty()) { + head_ = new_node; + tail_ = new_node; + } else { + tail_->next_ = new_node; + tail_ = new_node; + } + size_++; + return true; + } + + T* Remove(const T& element) { + Node* current = head_; + Node* previous = NULL; + if (IsEmpty()) { + return NULL; + } + + do { + if (element == current->object_) { + if (current == head_) { + head_ = head_->next_; + } + if (current == tail_) { + tail_ = previous; + } + if (previous != NULL) { + previous->next_ = current->next_; + } + size_--; + return ¤t->object_; + } + previous = current; + current = current->next_; + } while (current); + return NULL; + } + + bool Contains(const T& element) { + T value; + Iterator iterator(this); + while (iterator.HasNext()) { + iterator.GetNext(&value); + if (value == element) { + return true; + } + } + return false; + } + + bool IsEmpty() { + return head_ == NULL; + } + + int Size() { + return size_; + } + + private: + Node* head_; + Node* tail_; + int size_; +}; + +#endif // BIN_SET_H_ + diff --git a/runtime/bin/set_test.cc b/runtime/bin/set_test.cc new file mode 100644 index 00000000000..6a1491b2f99 --- /dev/null +++ b/runtime/bin/set_test.cc @@ -0,0 +1,125 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "bin/set.h" + +#include "vm/unit_test.h" + +UNIT_TEST_CASE(SetOperations) { + Set set; + EXPECT(set.IsEmpty()); + EXPECT(!set.Contains(1)); + EXPECT(set.Add(1)); + EXPECT(set.Contains(1)); + EXPECT(!set.IsEmpty()); + EXPECT(!set.Remove(2)); + EXPECT(!set.IsEmpty()); + EXPECT(set.Remove(1)); + EXPECT(set.IsEmpty()); + EXPECT(set.Add(3)); + EXPECT(set.Contains(3)); + EXPECT(!set.IsEmpty()); + EXPECT(set.Add(4)); + EXPECT(set.Contains(4)); + EXPECT(!set.IsEmpty()); + EXPECT(set.Add(5)); + EXPECT(set.Contains(5)); + EXPECT(set.Remove(5)); + EXPECT(set.Remove(4)); + EXPECT(set.Remove(3)); + EXPECT(set.IsEmpty()); + EXPECT(set.Add(1)); + EXPECT(set.Contains(1)); + EXPECT(set.Add(2)); + EXPECT(set.Contains(2)); + EXPECT(set.Add(3)); + EXPECT(set.Contains(3)); + EXPECT(!set.IsEmpty()); + EXPECT(set.Size() == 3); + EXPECT(set.Remove(2)); + EXPECT(set.Remove(1)); + EXPECT(set.Remove(3)); + EXPECT(set.IsEmpty()); + EXPECT(set.Size() == 0); + EXPECT(set.Add(1)); + EXPECT(set.Contains(1)); + EXPECT(set.Add(2)); + EXPECT(set.Contains(2)); + EXPECT(set.Add(3)); + EXPECT(set.Contains(3)); + EXPECT(!set.IsEmpty()); + EXPECT(set.Remove(2)); + EXPECT(set.Remove(3)); + EXPECT(set.Remove(1)); + EXPECT(set.IsEmpty()); + EXPECT(set.Add(1)); + EXPECT(set.Contains(1)); + EXPECT(set.Add(2)); + EXPECT(set.Contains(2)); + EXPECT(!set.IsEmpty()); + EXPECT(set.Remove(2)); + EXPECT(!set.IsEmpty()); + EXPECT(set.Add(3)); + EXPECT(set.Contains(3)); + EXPECT(set.Add(4)); + EXPECT(set.Contains(4)); + EXPECT(!set.Contains(2)); + EXPECT(!set.IsEmpty()); + EXPECT(set.Remove(3)); + EXPECT(!set.IsEmpty()); + EXPECT(set.Remove(4)); + EXPECT(set.Remove(1)); + EXPECT(set.IsEmpty()); + EXPECT(!set.Contains(4)); + EXPECT(set.Add(1)); + EXPECT(set.Contains(1)); + EXPECT(!set.IsEmpty()); + EXPECT(!set.Add(1)); + EXPECT(set.Size() == 1); + EXPECT(set.Contains(1)); + EXPECT(!set.IsEmpty()); + EXPECT(set.Add(2)); + EXPECT(set.Contains(2)); + EXPECT(!set.IsEmpty()); + EXPECT(!set.Add(2)); + EXPECT(set.Contains(2)); + EXPECT(!set.IsEmpty()); + EXPECT(set.Size() == 2); + EXPECT(set.Remove(1)); + EXPECT(set.Remove(2)); + EXPECT(set.IsEmpty()); + EXPECT(set.Size() == 0); +} + + +UNIT_TEST_CASE(SetIterator) { + Set set; + int i; + for (i = 1; i <= 10; i++) { + set.Add(i); + } + + Set::Iterator iterator(&set); + int value; + i = 0; + + while (iterator.HasNext()) { + iterator.GetNext(&value); + i++; + } + EXPECT(i == 10); + EXPECT(!set.IsEmpty()); + + Set emptyset; + Set::Iterator emptyiterator(&emptyset); + + i = 0; + while (emptyiterator.HasNext()) { + emptyiterator.GetNext(&value); + i++; + } + EXPECT(i == 0); + EXPECT(emptyset.IsEmpty()); +} + diff --git a/runtime/bin/snapshot_empty.cc b/runtime/bin/snapshot_empty.cc new file mode 100644 index 00000000000..abc5522ae8b --- /dev/null +++ b/runtime/bin/snapshot_empty.cc @@ -0,0 +1,16 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// This file is linked into the dart executable when it does not have a +// snapshot linked into it. + +#if defined(_WIN32) +typedef unsigned __int8 uint8_t; +#else +#include +#include +#endif +#include + +const uint8_t* snapshot_buffer = NULL; diff --git a/runtime/bin/snapshot_in.cc b/runtime/bin/snapshot_in.cc new file mode 100644 index 00000000000..b281cd582ab --- /dev/null +++ b/runtime/bin/snapshot_in.cc @@ -0,0 +1,22 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// This file is linked into the dart executable when it has a snapshot +// linked into it. + +#if defined(_WIN32) +typedef unsigned __int8 uint8_t; +#else +#include +#include +#endif +#include + +// The string on the next line will be filled in with the contents of the +// generated snapshot binary file. +// This string forms the content of a snapshot which is loaded in by dart. +static const uint8_t snapshot_buffer_[] = { + %s +}; +const uint8_t* snapshot_buffer = snapshot_buffer_; diff --git a/runtime/bin/socket.cc b/runtime/bin/socket.cc new file mode 100644 index 00000000000..026de7c817f --- /dev/null +++ b/runtime/bin/socket.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "bin/socket.h" +#include "bin/dartutils.h" + +#include "include/dart_api.h" + + +void FUNCTION_NAME(Socket_CreateConnect)(Dart_NativeArguments args) { + Dart_EnterScope(); + Dart_Handle socketobj = Dart_GetNativeArgument(args, 0); + const char* host = + DartUtils::GetStringValue(Dart_GetNativeArgument(args, 1)); + intptr_t port = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2)); + intptr_t socket = Socket::CreateConnect(host, port); + DartUtils::SetIntegerInstanceField( + socketobj, DartUtils::kIdFieldName, socket); + Dart_SetReturnValue(args, Dart_NewBoolean(socket >= 0)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(Socket_Available)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t socket = + DartUtils::GetIntegerInstanceField(Dart_GetNativeArgument(args, 0), + DartUtils::kIdFieldName); + intptr_t available = Socket::Available(socket); + Dart_SetReturnValue(args, Dart_NewInteger(available)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(Socket_ReadList)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t socket = + DartUtils::GetIntegerInstanceField(Dart_GetNativeArgument(args, 0), + DartUtils::kIdFieldName); + Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1); + assert(Dart_IsArray(buffer_obj)); + intptr_t offset = + DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2)); + intptr_t length = + DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 3)); + Dart_Result result = Dart_GetLength(buffer_obj); + assert(Dart_IsValidResult(result)); + assert((offset + length) <= Dart_GetResultAsCIntptr(result)); + + if (Dart_IsVMFlagSet("short_socket_read")) { + length = (length + 1) / 2; + } + + uint8_t* buffer = new uint8_t[length]; + intptr_t bytes_read = Socket::Read(socket, buffer, length); + if (bytes_read > 0) { + Dart_Result result = Dart_ArraySet(buffer_obj, offset, buffer, bytes_read); + ASSERT(Dart_IsValidResult(result)); + } else if (bytes_read < 0) { + bytes_read = 0; + } + delete[] buffer; + Dart_SetReturnValue(args, Dart_NewInteger(bytes_read)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(Socket_WriteList)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t socket = + DartUtils::GetIntegerInstanceField(Dart_GetNativeArgument(args, 0), + DartUtils::kIdFieldName); + Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1); + assert(Dart_IsArray(buffer_obj)); + intptr_t offset = + DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2)); + intptr_t length = + DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 3)); + Dart_Result result = Dart_GetLength(buffer_obj); + assert(Dart_IsValidResult(result)); + assert((offset + length) <= Dart_GetResultAsCIntptr(result)); + + if (Dart_IsVMFlagSet("short_socket_write")) { + length = (length + 1) / 2; + } + + uint8_t* buffer = new uint8_t[length]; + result = Dart_ArrayGet(buffer_obj, offset, buffer, length); + ASSERT(Dart_IsValidResult(result)); + intptr_t total_bytes_written = + Socket::Write(socket, reinterpret_cast(buffer), length); + delete[] buffer; + if (total_bytes_written < 0) { + total_bytes_written = 0; + } + Dart_SetReturnValue(args, Dart_NewInteger(total_bytes_written)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(Socket_GetPort)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t socket = + DartUtils::GetIntegerInstanceField(Dart_GetNativeArgument(args, 0), + DartUtils::kIdFieldName); + intptr_t port = Socket::GetPort(socket); + Dart_SetReturnValue(args, Dart_NewInteger(port)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(ServerSocket_CreateBindListen)(Dart_NativeArguments args) { + Dart_EnterScope(); + Dart_Handle socketobj = Dart_GetNativeArgument(args, 0); + const char* bindAddress = + DartUtils::GetStringValue(Dart_GetNativeArgument(args, 1)); + intptr_t port = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2)); + intptr_t backlog = + DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 3)); + intptr_t socket = + ServerSocket::CreateBindListen(bindAddress, port, backlog); + DartUtils::SetIntegerInstanceField( + socketobj, DartUtils::kIdFieldName, socket); + Dart_SetReturnValue(args, Dart_NewBoolean(socket >= 0)); + Dart_ExitScope(); +} + + +void FUNCTION_NAME(ServerSocket_Accept)(Dart_NativeArguments args) { + Dart_EnterScope(); + intptr_t socket = + DartUtils::GetIntegerInstanceField(Dart_GetNativeArgument(args, 0), + DartUtils::kIdFieldName); + Dart_Handle socketobj = Dart_GetNativeArgument(args, 1); + intptr_t newSocket = ServerSocket::Accept(socket); + if (newSocket >= 0) { + DartUtils::SetIntegerInstanceField( + socketobj, DartUtils::kIdFieldName, newSocket); + } + Dart_SetReturnValue(args, Dart_NewBoolean(newSocket >= 0)); + Dart_ExitScope(); +} diff --git a/runtime/bin/socket.dart b/runtime/bin/socket.dart new file mode 100644 index 00000000000..47de89f689d --- /dev/null +++ b/runtime/bin/socket.dart @@ -0,0 +1,116 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +interface ServerSocket factory _ServerSocket { + /* + * Constructs a new server socket, binds it to a given address and port, + * and listens on it. + */ + ServerSocket(String bindAddress, int port, int backlog); + + /* + * Accepts a connection to this socket. + */ + Socket accept(); + + /* + * The connection handler gets executed when there are incoming connections + * on the socket. + */ + void setConnectionHandler(void callback()); + + /* + * The error handler gets executed when a socket error occurs. + */ + void setErrorHandler(void callback()); + + /* + * Returns the port used by this socket. + */ + int get port(); + + /* + * Closes the socket. + */ + void close(); +} + + +interface Socket factory _Socket { + /* + * Constructs a new socket and connects it to the given host on the given + * port. + */ + Socket(String host, int port); + + /* + * Returns the number of received and non-read bytes in the socket that + * can be read. + */ + int available(); + + /* + * Reads up to [count] bytes of data from the socket and stores them into + * buffer after buffer offset [offset]. The number of successfully read + * bytes is returned. This function is non-blocking and will only read data + * if data is available. + */ + int readList(List buffer, int offset, int count); + + /* + * Writes up to [count] bytes of the buffer from [offset] buffer offset to + * the socket. The number of successfully written bytes is returned. This + * function is non-blocking and will only write data if buffer space is + * available in the socket. It will return 0 if an error occurs, e.g., no + * buffer space available. + */ + int writeList(List buffer, int offset, int count); + + /* + * The connect handler gets executed when connection to a given host + * succeeded. + */ + void setConnectHandler(void callback()); + + /* + * The data handler gets executed when data becomes available at the socket. + */ + void setDataHandler(void callback()); + + /* + * The write handler gets executed when the socket becomes available for + * writing. + */ + void setWriteHandler(void callback()); + + /* + * The error handler gets executed when a socket error occurs. + */ + void setErrorHandler(void callback()); + + /* + * The close handler gets executed when the socket was closed. + */ + void setCloseHandler(void callback()); + + /* + * Returns input stream to the socket. + */ + InputStream get inputStream(); + + /* + * Returns output stream of the socket. + */ + OutputStream get outputStream(); + + /* + * Returns the port used by this socket. + */ + int get port(); + + /* + * Closes the socket + */ + void close(); +} diff --git a/runtime/bin/socket.h b/runtime/bin/socket.h new file mode 100644 index 00000000000..257ca869588 --- /dev/null +++ b/runtime/bin/socket.h @@ -0,0 +1,37 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_SOCKET_H_ +#define BIN_SOCKET_H_ + +#include "bin/builtin.h" +#include "bin/globals.h" + + +class Socket { + public: + static bool Initialize(); + static intptr_t Available(intptr_t fd); + static intptr_t Read(intptr_t fd, void* buffer, intptr_t num_bytes); + static intptr_t Write(intptr_t fd, const void* buffer, intptr_t num_bytes); + static intptr_t CreateConnect(const char* host, const intptr_t port); + static intptr_t GetPort(intptr_t fd); + + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(Socket); +}; + + +class ServerSocket { + public: + static intptr_t Accept(intptr_t fd); + static intptr_t CreateBindListen(const char* bindAddress, + intptr_t port, + intptr_t backlog); + + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(ServerSocket); +}; + +#endif // BIN_SOCKET_H_ diff --git a/runtime/bin/socket_impl.dart b/runtime/bin/socket_impl.dart new file mode 100644 index 00000000000..af40dbf82c8 --- /dev/null +++ b/runtime/bin/socket_impl.dart @@ -0,0 +1,287 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class SocketIOException { + const SocketIOException([String message = ""]) : message_ = message; + String getMessage() { return message_; } + final String message_; +} + +/* + * SocketNativeWrapper is defined in native and holds a field to store the + * native socket object. + */ +class SocketBase { + + /* + * Keep these constants in sync with the native poll event identifiers. + */ + static final int _IN_EVENT = 0; + static final int _OUT_EVENT = 1; + static final int _ERROR_EVENT = 2; + static final int _CLOSE_EVENT = 3; + static final int _CLOSE_COMMAND = 4; + + SocketBase () { + _handler = new ReceivePort(); + _handlerMap = new List(_CLOSE_EVENT + 1); + _handlerMask = 0; + _canActivateHandlers = true; + _id = 0; + _handler.receive((var message, ignored) { + _multiplex(message); + }); + EventHandler._start(); + } + + /* + * Multiplexes socket events to the right socket handler. + */ + void _multiplex(List message) { + assert(message.length == 1); + _canActivateHandlers = false; + int event_mask = message[0]; + for (int i = _IN_EVENT; i <= _CLOSE_EVENT; i++) { + if (((event_mask & (1 << i)) != 0) && _handlerMap[i] !== null) { + var handleEvent = _handlerMap[i]; + /* + * Unregister the out handler before executing it. + */ + if (i == _OUT_EVENT) { + _setHandler(i, null); + } + handleEvent(); + } + } + _canActivateHandlers = true; + _doActivateHandlers(); + } + + void _setHandler(int event, void callback()) { + if (callback === null) { + _handlerMask &= ~(1 << event); + } else { + _handlerMask |= (1 << event); + } + _handlerMap[event] = callback; + _doActivateHandlers(); + } + + void _getPort() native "Socket_GetPort"; + + void setErrorHandler(void callback()) { + _setHandler(_ERROR_EVENT, callback); + } + + void _doActivateHandlers() { + if (_canActivateHandlers && (_id >= 0)) { + EventHandler._sendData(_id, _handler, _handlerMask); + } + } + + int get port() { + if (_port == null) { + _port = _getPort(); + } + return _port; + } + + void close() { + if (_id >= 0) { + EventHandler._sendData(_id, _handler, 1 << _CLOSE_COMMAND); + _handler.close(); + _id = -1; + } else { + throw new + SocketIOException("Error: close failed - invalid socket handle"); + } + } + + + /* + * Socket id is set from native. -1 indicates that the socket was closed. + */ + int _id; + + /* + * Dedicated ReceivePort for socket events. + */ + ReceivePort _handler; + + /* + * Poll event to handler map. + */ + List _handlerMap; + + /* + * Indicates for which poll events the socket registered handlers. + */ + int _handlerMask; + + /* + * Indicates if native interrupts can be activated. + */ + bool _canActivateHandlers; + + /* + * Holds the port of the socket, null if not known. + */ + int _port; +} + + +class _ServerSocket extends SocketBase implements ServerSocket { + /* + * Constructor for server socket. First a socket object is allocated + * in which the native socket is stored. After that _createBind + * is called which creates a file descriptor and binds the given address + * and port to the socket. Null is returned if file descriptor creation or + * bind failed. + */ + factory _ServerSocket(String bindAddress, int port, int backlog) { + ServerSocket socket = new _ServerSocket._internal(); + if (!socket._createBindListen(bindAddress, port, backlog)) { + return null; + } + if (port != 0) { + socket._port = port; + } + return socket; + } + + _ServerSocket._internal() : super() {} + + Socket accept() { + if (_id >= 0) { + _Socket socket = new _Socket._internal(); + if (_accept(socket)) { + return socket; + } + return null; + } + throw new + SocketIOException("Error: accept failed - invalid socket handle"); + } + + bool _accept(Socket socket) native "ServerSocket_Accept"; + + bool _createBindListen(String bindAddress, int port, int backlog) + native "ServerSocket_CreateBindListen"; + + void setConnectionHandler(void callback()) { + _setHandler(_IN_EVENT, callback); + } +} + + +class _Socket extends SocketBase implements Socket { + /* + * Constructor for socket. First a socket object is allocated + * in which the native socket is stored. After that _createConnect is + * called which creates a file discriptor and connects to the given + * host on the given port. Null is returned if file descriptor creation + * or connect failsed + */ + factory _Socket(String host, int port) { + Socket socket = new _Socket._internal(); + if (!socket._createConnect(host, port)) { + return null; + } + return socket; + } + + _Socket._internal() : super() {} + + int available() { + if (_id >= 0) { + return _available(); + } + throw new + SocketIOException("Error: available failed - invalid socket handle"); + } + + int _available() native "Socket_Available"; + + int readList(List buffer, int offset, int bytes) { + if (_id >= 0) { + if (bytes == 0) { + return 0; + } + if (offset < 0) { + throw new IndexOutOfRangeException(offset); + } + if (bytes < 0) { + throw new IndexOutOfRangeException(bytes); + } + if ((offset + bytes) > buffer.length) { + throw new IndexOutOfRangeException(offset + bytes); + } + return _readList(buffer, offset, bytes); + } + throw new + SocketIOException("Error: readList failed - invalid socket handle"); + } + + int _readList(List buffer, int offset, int bytes) + native "Socket_ReadList"; + + int writeList(List buffer, int offset, int bytes) { + if (_id >= 0) { + if (bytes == 0) { + return 0; + } + if (offset < 0) { + throw new IndexOutOfRangeException(offset); + } + if (bytes < 0) { + throw new IndexOutOfRangeException(bytes); + } + if ((offset + bytes) > buffer.length) { + throw new IndexOutOfRangeException(offset + bytes); + } + return _writeList(buffer, offset, bytes); + } + throw new + SocketIOException("Error: writeList failed - invalid socket handle"); + } + + int _writeList(List buffer, int offset, int bytes) + native "Socket_WriteList"; + + bool _createConnect(String host, int port) native "Socket_CreateConnect"; + + void setWriteHandler(void callback()) { + _setHandler(_OUT_EVENT, callback); + } + + void setConnectHandler(void callback()) { + _setHandler(_OUT_EVENT, callback); + } + + void setDataHandler(void callback()) { + _setHandler(_IN_EVENT, callback); + } + + void setCloseHandler(void callback()) { + _setHandler(_CLOSE_EVENT, callback); + } + + InputStream get inputStream() { + if (_inputStream === null) { + _inputStream = new SocketInputStream(this); + } + return _inputStream; + } + + OutputStream get outputStream() { + if (_outputStream === null) { + _outputStream = new SocketOutputStream(this); + } + return _outputStream; + } + + SocketInputStream _inputStream; + SocketOutputStream _outputStream; +} + diff --git a/runtime/bin/socket_linux.cc b/runtime/bin/socket_linux.cc new file mode 100644 index 00000000000..c50117acd75 --- /dev/null +++ b/runtime/bin/socket_linux.cc @@ -0,0 +1,138 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bin/fdutils.h" +#include "bin/socket.h" + + +bool Socket::Initialize() { + // Nothing to do on Linux. + return true; +} + + +intptr_t Socket::CreateConnect(const char* host, const intptr_t port) { + intptr_t fd; + struct hostent* server; + struct sockaddr_in server_address; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + fprintf(stderr, "Error CreateConnect: %s\n", strerror(errno)); + return -1; + } + + FDUtils::SetNonBlocking(fd); + + server = gethostbyname(host); + if (server == NULL) { + close(fd); + fprintf(stderr, "Error CreateConnect: %s\n", strerror(errno)); + return -1; + } + + server_address.sin_family = AF_INET; + server_address.sin_port = htons(port); + bcopy(server->h_addr, &server_address.sin_addr.s_addr, server->h_length); + memset(&server_address.sin_zero, 0, sizeof(server_address.sin_zero)); + intptr_t result = connect(fd, + reinterpret_cast(&server_address), + sizeof(server_address)); + if (result == 0 || errno == EINPROGRESS) { + return fd; + } + return -1; +} + + +intptr_t Socket::Available(intptr_t fd) { + return FDUtils::AvailableBytes(fd); +} + + +intptr_t Socket::Read(intptr_t fd, + void* buffer, + intptr_t num_bytes) { + assert(fd >= 0); + intptr_t read_bytes = read(fd, buffer, num_bytes); + return read_bytes; +} + + +intptr_t Socket::Write(intptr_t fd, const void* buffer, intptr_t num_bytes) { + assert(fd >= 0); + return write(fd, buffer, num_bytes); +} + + +intptr_t Socket::GetPort(intptr_t fd) { + assert(fd >= 0); + struct sockaddr_in socket_address; + socklen_t size = sizeof(socket_address); + if (getsockname(fd, reinterpret_cast(&socket_address), + &size)) { + fprintf(stderr, "Error getsockname: %s\n", strerror(errno)); + return 0; + } + return ntohs(socket_address.sin_port); +} + + +intptr_t ServerSocket::CreateBindListen(const char* host, + intptr_t port, + intptr_t backlog) { + intptr_t fd; + struct sockaddr_in server_address; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + fprintf(stderr, "Error CreateBind: %s\n", strerror(errno)); + return -1; + } + + int optval = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + + server_address.sin_family = AF_INET; + server_address.sin_port = htons(port); + server_address.sin_addr.s_addr = inet_addr(host); + memset(&server_address.sin_zero, 0, sizeof(server_address.sin_zero)); + + if (bind(fd, reinterpret_cast(&server_address), + sizeof(server_address)) < 0) { + close(fd); + fprintf(stderr, "Error Bind: %s\n", strerror(errno)); + return -1; + } + + if (listen(fd, backlog) != 0) { + fprintf(stderr, "Error Listen: %s\n", strerror(errno)); + return -1; + } + + FDUtils::SetNonBlocking(fd); + return fd; +} + +intptr_t ServerSocket::Accept(intptr_t fd) { + intptr_t socket; + struct sockaddr clientaddr; + socklen_t addrlen = sizeof(clientaddr); + socket = accept(fd, &clientaddr, &addrlen); + if (socket < 0) { + fprintf(stderr, "Error Accept: %s\n", strerror(errno)); + } else { + FDUtils::SetNonBlocking(socket); + } + return socket; +} diff --git a/runtime/bin/socket_macos.cc b/runtime/bin/socket_macos.cc new file mode 100644 index 00000000000..231bcccb93c --- /dev/null +++ b/runtime/bin/socket_macos.cc @@ -0,0 +1,138 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bin/fdutils.h" +#include "bin/socket.h" + + +bool Socket::Initialize() { + // Nothing to do on Mac OS. + return true; +} + + +intptr_t Socket::CreateConnect(const char* host, const intptr_t port) { + intptr_t fd; + struct hostent* server; + struct sockaddr_in server_address; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + fprintf(stderr, "Error CreateConnect: %s\n", strerror(errno)); + return -1; + } + + FDUtils::SetNonBlocking(fd); + + server = gethostbyname(host); + if (server == NULL) { + close(fd); + fprintf(stderr, "Error CreateConnect: %s\n", strerror(errno)); + return -1; + } + + server_address.sin_family = AF_INET; + server_address.sin_port = htons(port); + bcopy(server->h_addr, &server_address.sin_addr.s_addr, server->h_length); + memset(&server_address.sin_zero, 0, sizeof(server_address.sin_zero)); + intptr_t result = connect(fd, + reinterpret_cast(&server_address), + sizeof(server_address)); + if (result == 0 || errno == EINPROGRESS) { + return fd; + } + return -1; +} + + +intptr_t Socket::Available(intptr_t fd) { + return FDUtils::AvailableBytes(fd); +} + + +intptr_t Socket::Read(intptr_t fd, + void* buffer, + intptr_t num_bytes) { + assert(fd >= 0); + intptr_t read_bytes = read(fd, buffer, num_bytes); + return read_bytes; +} + + +intptr_t Socket::Write(intptr_t fd, const void* buffer, intptr_t num_bytes) { + assert(fd >= 0); + return write(fd, buffer, num_bytes); +} + + +intptr_t Socket::GetPort(intptr_t fd) { + assert(fd >= 0); + struct sockaddr_in socket_address; + socklen_t size = sizeof(socket_address); + if (getsockname(fd, reinterpret_cast(&socket_address), + &size)) { + fprintf(stderr, "Error getsockname: %s\n", strerror(errno)); + return 0; + } + return ntohs(socket_address.sin_port); +} + + +intptr_t ServerSocket::CreateBindListen(const char* host, + intptr_t port, + intptr_t backlog) { + intptr_t fd; + struct sockaddr_in server_address; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + fprintf(stderr, "Error CreateBind: %s\n", strerror(errno)); + return -1; + } + + int optval = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + + server_address.sin_family = AF_INET; + server_address.sin_port = htons(port); + server_address.sin_addr.s_addr = inet_addr(host); + memset(&server_address.sin_zero, 0, sizeof(server_address.sin_zero)); + + if (bind(fd, reinterpret_cast(&server_address), + sizeof(server_address)) < 0) { + close(fd); + fprintf(stderr, "Error Bind: %s\n", strerror(errno)); + return -1; + } + + if (listen(fd, backlog) != 0) { + fprintf(stderr, "Error Listen: %s\n", strerror(errno)); + return -1; + } + + FDUtils::SetNonBlocking(fd); + return fd; +} + +intptr_t ServerSocket::Accept(intptr_t fd) { + intptr_t socket; + struct sockaddr clientaddr; + socklen_t addrlen = sizeof(clientaddr); + socket = accept(fd, &clientaddr, &addrlen); + if (socket < 0) { + fprintf(stderr, "Error Accept: %s\n", strerror(errno)); + } else { + FDUtils::SetNonBlocking(socket); + } + return socket; +} diff --git a/runtime/bin/socket_stream.dart b/runtime/bin/socket_stream.dart new file mode 100644 index 00000000000..09d94f63f3e --- /dev/null +++ b/runtime/bin/socket_stream.dart @@ -0,0 +1,166 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class SocketInputStream implements InputStream { + SocketInputStream(Socket socket) { + _socket = socket; + _buffer = null; + } + + bool read(List buffer, int offset, int len, void callback()) { + // Read data just out of the buffer. + if (_buffer !== null && len <= _buffer.length) { + buffer.copyFrom(_buffer, 0, offset, len); + int remainder = _buffer.length - len; + if (remainder > 0) { + List newBuffer = new List(remainder); + newBuffer.copyFrom(_buffer, 0, len, remainder); + _buffer = newBuffer; + } else { + _buffer = null; + } + return true; + } + // Read data out of the buffer if available and from the socket. + else { + int bytesRead = 0; + if (_buffer !== null) { + buffer.copyFrom(_buffer, offset, 0, _buffer.length); + bytesRead = _buffer.length; + _buffer = null; + } + + bytesRead += + _socket.readList(buffer, offset + bytesRead, len - bytesRead); + + if (bytesRead == len) { + return true; + } + + void doRead() { + bytesRead += + _socket.readList(buffer, offset + bytesRead, len - bytesRead); + if (bytesRead < len) { + _socket.setDataHandler(doRead); + } else { + assert(bytesRead == len); + _socket.setDataHandler(null); + if (callback !== null) { + callback(); + } + } + } + + _socket.setDataHandler(doRead); + return false; + } + } + + int _matchPattern(List buffer, List pattern, int start) { + int j; + if (pattern.length > buffer.length) { + return -1; + } + for (int i = start; i < (buffer.length - pattern.length + 1); i++) { + for (j = 0; j < pattern.length; j++) { + if (buffer[i + j] != pattern[j]) { + break; + } + } + if (j == pattern.length) { + return i; + } + } + return -1; + } + + /* + * Appends the newBuffer to the buffer (if available), sets the buffer to + * null, and returns the merged buffer. + */ + List _getBufferedData(List newBuffer, int appendingBufferSpace) { + List buffer; + int newDataStart = 0; + if (_buffer !== null) { + buffer = new List(_buffer.length + appendingBufferSpace); + buffer.copyFrom(_buffer, 0, 0, _buffer.length); + newDataStart = _buffer.length; + _buffer = null; + } else { + buffer = new List(appendingBufferSpace); + } + buffer.copyFrom(newBuffer, 0, newDataStart, appendingBufferSpace); + return buffer; + } + + void readUntil(List pattern, void callback(List buffer)) { + void doRead() { + int size = _socket.available(); + List buffer = new List(size); + int result = _socket.readList(buffer, 0, size); + if (result > 0) { + // TODO(hpayer): Avoid copying of data before pattern matching. + List newBuffer = _getBufferedData(buffer, result); + int index = _matchPattern(newBuffer, pattern, 0); + // If pattern was found return the data including pattern and store the + // remainder in the buffer. + if (index != -1) { + int finalBufferSize = index + pattern.length; + List finalBuffer = new List(finalBufferSize); + finalBuffer.copyFrom(newBuffer, 0, 0, finalBufferSize); + if (finalBufferSize < newBuffer.length) { + List remainder = + new List(newBuffer.length - finalBufferSize); + remainder.copyFrom(buffer, finalBufferSize, 0, newBuffer.length); + _buffer = remainder; + } + _socket.setDataHandler(null); + callback(finalBuffer); + } else { + _buffer = newBuffer; + } + } + } + _socket.setDataHandler(doRead); + } + + Socket _socket; + + /* + * Read and readUntil read data out of that buffer first before reading new + * data out of the socket. + */ + List _buffer; +} + +class SocketOutputStream implements OutputStream { + SocketOutputStream(Socket socket) { + _socket = socket; + } + + bool write(List buffer, int offset, int len, void callback()) { + int bytesWritten = _socket.writeList(buffer, offset, len); + + void finishWrite() { + bytesWritten += _socket.writeList( + buffer, offset + bytesWritten, len - bytesWritten); + if (bytesWritten < len) { + _socket.setWriteHandler(finishWrite); + } else { + assert(bytesWritten == len); + if (callback !== null) { + callback(); + } + } + } + + if (bytesWritten == len) { + return true; + } + _socket.setWriteHandler(finishWrite); + return false; + } + + Socket _socket; +} diff --git a/runtime/bin/socket_win.cc b/runtime/bin/socket_win.cc new file mode 100644 index 00000000000..4e7e2fd3909 --- /dev/null +++ b/runtime/bin/socket_win.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include + +#include "bin/builtin.h" +#include "bin/eventhandler.h" +#include "bin/socket.h" + +bool Socket::Initialize() { + int err; + WSADATA winsock_data; + WORD version_requested = MAKEWORD(1, 0); + err = WSAStartup(version_requested, &winsock_data); + if (err != 0) { + fprintf(stderr, "Unable to initialize Winsock: %d\n", WSAGetLastError()); + } + return err == 0; +} + +intptr_t Socket::Available(intptr_t fd) { + ClientSocket* client_socket = reinterpret_cast(fd); + return client_socket->Available(); +} + + +intptr_t Socket::Read(intptr_t fd, void* buffer, intptr_t num_bytes) { + ASSERT(reinterpret_cast(fd)->is_client_socket()); + ClientSocket* client_socket = reinterpret_cast(fd); + return client_socket->Read(buffer, num_bytes); +} + + +intptr_t Socket::Write(intptr_t fd, const void* buffer, intptr_t num_bytes) { + ASSERT(reinterpret_cast(fd)->is_client_socket()); + ClientSocket* client_socket = reinterpret_cast(fd); + return client_socket->Write(buffer, num_bytes); +} + + +intptr_t Socket::GetPort(intptr_t fd) { + ASSERT(reinterpret_cast(fd)->is_socket()); + SocketHandle* socket_handle = reinterpret_cast(fd); + struct sockaddr_in socket_address; + socklen_t size = sizeof(socket_address); + if (getsockname(socket_handle->socket(), + reinterpret_cast(&socket_address), + &size)) { + fprintf(stderr, "Error getsockname: %s\n", strerror(errno)); + return 0; + } + return ntohs(socket_address.sin_port); +} + + +intptr_t Socket::CreateConnect(const char* host, const intptr_t port) { + SOCKET s = socket(AF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) { + fprintf(stderr, "Error CreateConnect: %d\n", WSAGetLastError()); + return -1; + } + + struct hostent* server = gethostbyname(host); + if (server == NULL) { + fprintf(stderr, "Error CreateConnect: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + + struct sockaddr_in server_address; + server_address.sin_family = AF_INET; + server_address.sin_port = htons(port); + server_address.sin_addr.s_addr = inet_addr(host); + int status = connect( + s, + reinterpret_cast(&server_address), + sizeof(server_address)); + if (status == SOCKET_ERROR) { + fprintf(stderr, "Error CreateConnect: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + + ClientSocket* client_socket = new ClientSocket(s); + return reinterpret_cast(client_socket); +} + + +intptr_t ServerSocket::Accept(intptr_t fd) { + ListenSocket* listen_socket = reinterpret_cast(fd); + ClientSocket* client_socket = listen_socket->Accept(); + if (client_socket != NULL) { + return reinterpret_cast(client_socket); + } else { + return -1; + } +} + + +intptr_t ServerSocket::CreateBindListen(const char* host, + intptr_t port, + intptr_t backlog) { + SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == INVALID_SOCKET) { + fprintf(stderr, "Error CreateBindListen: %d\n", WSAGetLastError()); + return -1; + } + + BOOL optval = true; + int status = setsockopt(s, + SOL_SOCKET, + SO_REUSEADDR, + reinterpret_cast(&optval), + sizeof(optval)); + if (status == SOCKET_ERROR) { + fprintf(stderr, "Error CreateBindListen: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + + sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(host); + addr.sin_port = htons(port); + status = bind(s, + reinterpret_cast(&addr), + sizeof(addr)); + if (status == SOCKET_ERROR) { + fprintf(stderr, "Error CreateBindListen: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + + status = listen(s, backlog); + if (status == SOCKET_ERROR) { + fprintf(stderr, "Error socket listen: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + + ListenSocket* listen_socket = new ListenSocket(s); + return reinterpret_cast(listen_socket); +} diff --git a/runtime/bin/timer.dart b/runtime/bin/timer.dart new file mode 100644 index 00000000000..34ea2c038c7 --- /dev/null +++ b/runtime/bin/timer.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +interface Timer factory _Timer{ + + /* + * Creates a new timer. The [callback] callback is invoked after + * [milliSeconds] milliseconds. If [repeating] is set, the timer + * is repeated every [milliSeconds] milliseconds until cancelled. + */ + Timer(void callback(Timer timer), int milliSeconds, bool repeating); + + /* + * Cancels the timer. + */ + void cancel(); +} + diff --git a/runtime/bin/timer_impl.dart b/runtime/bin/timer_impl.dart new file mode 100644 index 00000000000..da23c6c5666 --- /dev/null +++ b/runtime/bin/timer_impl.dart @@ -0,0 +1,162 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class _Timer implements Timer { + + /* + * Set jitter to wake up timer events that would happen in _TIMER_JITTER ms. + */ + static final int _TIMER_JITTER = 0; + + /* + * Disables the timer. + */ + static final int _NO_TIMER = -1; + + factory _Timer(void callback(Timer timer), + int milliSeconds, + bool repeating) { + EventHandler._start(); + if (_timers === null) { + _timers = new DoubleLinkedQueue<_Timer>(); + } + Timer timer = new _Timer._internal(); + timer._callback = callback; + timer._milliSeconds = milliSeconds; + timer._wakeupTime = (new DateTime.now()).value + milliSeconds; + timer._repeating = repeating; + timer._addTimerToList(); + timer._notifyEventHandler(); + return timer; + } + + _Timer._internal() {} + + void _clear() { + _callback = null; + _milliSeconds = 0; + _wakeupTime = 0; + _repeating = false; + } + + /* + * Cancels a set timer. The timer is removed from the timer list and if + * the given timer is the earliest timer the native timer is reset. + */ + void cancel() { + _clear(); + DoubleLinkedQueueEntry<_Timer> entry = _timers.firstEntry(); + DoubleLinkedQueueEntry<_Timer> first = _timers.firstEntry(); + + while (entry !== null) { + if (entry.element === this) { + entry.remove(); + if (first.element == this) { + entry = _timers.firstEntry(); + _notifyEventHandler(); + } + return; + } + entry = entry.nextEntry(); + } + } + + void _advanceWakeupTime() { + _wakeupTime += _milliSeconds; + } + + /* + * Adds a timer to the timer list and resets the native timer if it is the + * earliest timer in the list. Timers with the same wakeup time are enqueued + * in order and notified in FIFO order. + */ + void _addTimerToList() { + if (_callback !== null) { + + DoubleLinkedQueueEntry<_Timer> entry = _timers.firstEntry(); + if (entry === null) { + _createTimerHandler(); + } + + while (entry !== null) { + if (_wakeupTime < entry.element._wakeupTime) { + entry.prepend(this); + return; + } + entry = entry.nextEntry(); + } + _timers.addLast(this); + } + } + + + void _notifyEventHandler() { + if (_timers.firstEntry() === null) { + EventHandler._sendData(-1, _receivePort, _NO_TIMER); + _shutdownTimerHandler(); + } else { + EventHandler._sendData(-1, + _receivePort, + _timers.firstEntry().element._wakeupTime); + } + } + + + /* + * Creates a receive port and registers the timer handler on that receive + * port. + */ + void _createTimerHandler() { + + void _handleTimeout() { + int currentTime = (new DateTime.now()).value + _TIMER_JITTER; + + DoubleLinkedQueueEntry<_Timer> entry = _timers.firstEntry(); + DoubleLinkedQueueEntry<_Timer> current; + while (entry !== null) { + current = entry; + _Timer timer = current.element; + entry = entry.nextEntry(); + if (timer._wakeupTime <= currentTime) { + current.remove(); + (current.element._callback)(current.element); + if (current.element._repeating) { + current.element._advanceWakeupTime(); + timer._addTimerToList(); + _notifyEventHandler(); + } + } else { + break; + } + } + _notifyEventHandler(); + } + + if(_receivePort === null) { + _receivePort = new ReceivePort(); + _receivePort.receive((var message, ignored) { + _handleTimeout(); + }); + } + } + + void _shutdownTimerHandler() { + _receivePort.close(); + _receivePort = null; + } + + + /* + * Timers are ordered by wakeup time. + */ + static DoubleLinkedQueue<_Timer> _timers; + + static ReceivePort _receivePort; + + var _callback; + int _milliSeconds; + int _wakeupTime; + bool _repeating; +} + diff --git a/runtime/codereview.settings b/runtime/codereview.settings new file mode 100644 index 00000000000..ae0f11ec466 --- /dev/null +++ b/runtime/codereview.settings @@ -0,0 +1,4 @@ +# This file is used by gcl to get repository specific information. +CODE_REVIEW_SERVER: https://chromereviews.googleplex.com +VIEW_VC: https://code.google.com/p/dart/source/detail?r= +CC_LIST: diff --git a/runtime/dart-runtime.gyp b/runtime/dart-runtime.gyp new file mode 100644 index 00000000000..825275bb4d1 --- /dev/null +++ b/runtime/dart-runtime.gyp @@ -0,0 +1,33 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +{ + 'includes': [ + 'vm/vm.gypi', + 'bin/bin.gypi', + 'third_party/jscre/jscre.gypi', + 'tools/gyp/runtime-configurations.gypi', + ], + 'targets': [ + { + 'target_name': 'libdart', + 'type': 'shared_library', + 'dependencies': [ + 'libdart_lib', + 'libdart_vm', + 'libjscre', + ], + 'include_dirs': [ + '.', + ], + 'defines': [ + 'DART_SHARED_LIB', + ], + 'sources': [ + 'include/dart_api.h', + 'vm/dart_api_impl.cc', + ], + }, + ], +} diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h new file mode 100755 index 00000000000..bd652fb955f --- /dev/null +++ b/runtime/include/dart_api.h @@ -0,0 +1,395 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef INCLUDE_DART_API_H_ +#define INCLUDE_DART_API_H_ + +#ifdef __cplusplus +#define DART_EXTERN_C extern "C" +#else +#define DART_EXTERN_C +#endif + +#if defined(__CYGWIN__) +#error Tool chain and platform not supported. +#elif defined(_WIN32) +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; +typedef signed __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#if defined(DART_SHARED_LIB) +#define DART_EXPORT DART_EXTERN_C __declspec(dllexport) +#else +#define DART_EXPORT DART_EXTERN_C +#endif +#else +#include +#if __GNUC__ >= 4 +#if defined(DART_SHARED_LIB) +#define DART_EXPORT DART_EXTERN_C __attribute__ ((visibility("default"))) +#else +#define DART_EXPORT DART_EXTERN_C +#endif +#else +#error Tool chain not supported. +#endif +#endif + +#include + +typedef void* Dart_Isolate; +typedef void* Dart_Handle; +typedef void* Dart_NativeArguments; +typedef enum { + kRetCIntptr = 0, + kRetCBool, + kRetCString, + kRetObject, + kRetCInt64, + kRetCDouble, + kRetLibSpec, + kRetError, +} Dart_ReturnType; + +typedef struct { + Dart_ReturnType type_; + union { + intptr_t int_value; + bool bool_value; + const char* str_value; + Dart_Handle obj_value; + int64_t int64_value; + double double_value; + const char* errmsg; + } retval_; +} Dart_Result; + +typedef enum { + kLibraryTag = 0, + kImportTag, + kSourceTag, + kCanonicalizeUrl, +} Dart_LibraryTag; + +typedef void Dart_Snapshot; + +typedef int64_t Dart_Port; + +// Allow the embedder to intercept isolate creation. Both at startup and when +// spawning new isolates from Dart code. +// The result returned from this callback is handed to all isolates spawned +// from the isolate currently being initialized. +// Return NULL if an error is encountered. The isolate being initialized will +// be shutdown. No Dart code will execute in before it is shutdown. +// TODO(iposva): Pass a specification of the app file being spawned. +typedef void* (*Dart_IsolateInitCallback)(void* data); + +typedef void (*Dart_NativeFunction)(Dart_NativeArguments arguments); +typedef Dart_NativeFunction (*Dart_NativeEntryResolver)(Dart_Handle name, + int num_of_arguments); +typedef Dart_Result (*Dart_LibraryTagHandler)(Dart_LibraryTag tag, + Dart_Handle library, + Dart_Handle url); + +// TODO(iposva): This is a placeholder for the eventual external Dart API. + +// Return value handling after a Dart API call. +inline bool Dart_IsValidResult(const Dart_Result& result) { + return (result.type_ != kRetError); +} +inline intptr_t Dart_GetResultAsCIntptr(const Dart_Result& result) { + assert(result.type_ == kRetCIntptr); // Valid to access result as C int. + return result.retval_.int_value; +} +inline Dart_Result Dart_ResultAsCIntptr(intptr_t value) { + Dart_Result result; + result.type_ = kRetCIntptr; + result.retval_.int_value = value; + return result; +} +inline bool Dart_GetResultAsCBoolean(const Dart_Result& result) { + assert(result.type_ == kRetCBool); // Valid to access result as C bool. + return result.retval_.bool_value; +} +inline Dart_Result Dart_ResultAsCBoolean(bool value) { + Dart_Result result; + result.type_ = kRetCBool; + result.retval_.bool_value = value; + return result; +} +inline const char* Dart_GetResultAsCString(const Dart_Result& result) { + assert(result.type_ == kRetCString); // Valid to access result as C string. + return result.retval_.str_value; +} +inline Dart_Result Dart_ResultAsCString(const char* value) { + // Assumes passed in string value will be available on return. + Dart_Result result; + result.type_ = kRetCString; + result.retval_.str_value = value; + return result; +} +inline Dart_Handle Dart_GetResult(const Dart_Result& result) { + assert(result.type_ == kRetObject); // Valid to access result as Dart obj. + return result.retval_.obj_value; +} +inline Dart_Result Dart_ResultAsObject(Dart_Handle value) { + Dart_Result result; + result.type_ = kRetObject; + result.retval_.obj_value = value; + return result; +} +inline int64_t Dart_GetResultAsCInt64(const Dart_Result& result) { + assert(result.type_ == kRetCInt64); // Valid to access result as C int64_t. + return result.retval_.int64_value; +} +inline Dart_Result Dart_ResultAsCInt64(int64_t value) { + Dart_Result result; + result.type_ = kRetCInt64; + result.retval_.int64_value = value; + return result; +} +inline double Dart_GetResultAsCDouble(const Dart_Result& result) { + assert(result.type_ == kRetCDouble); // Valid to access result as C double. + return result.retval_.double_value; +} +inline Dart_Result Dart_ResultAsCDouble(double value) { + Dart_Result result; + result.type_ = kRetCDouble; + result.retval_.double_value = value; + return result; +} +inline const char* Dart_GetErrorCString(const Dart_Result& result) { + assert(result.type_ == kRetError); // Valid to access only on failure. + return result.retval_.errmsg; +} +inline Dart_Result Dart_ErrorResult(const char* value) { + // Assumes passed in string value will be available on return. + Dart_Result result; + result.type_ = kRetError; + result.retval_.errmsg = value; + return result; +} + + +// Initialize the VM with commmand line flags. +DART_EXPORT bool Dart_Initialize(int argc, char** argv, + Dart_IsolateInitCallback callback); + + +// Isolate handling. +DART_EXPORT Dart_Isolate Dart_CreateIsolate(const Dart_Snapshot* snapshot, + void* data); +DART_EXPORT void Dart_ShutdownIsolate(); + +DART_EXPORT Dart_Isolate Dart_CurrentIsolate(); +DART_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate); +DART_EXPORT void Dart_ExitIsolate(); + +DART_EXPORT void Dart_RunLoop(); + + +// Object. +DART_EXPORT Dart_Result Dart_ObjectToString(Dart_Handle object); +DART_EXPORT bool Dart_IsNull(Dart_Handle object); + + +// Returns true if the two objects are equal. +DART_EXPORT Dart_Result Dart_Objects_Equal(Dart_Handle obj1, Dart_Handle obj2); + + +// Classes. +DART_EXPORT Dart_Result Dart_GetClass(Dart_Handle library, Dart_Handle name); +DART_EXPORT Dart_Result Dart_IsInstanceOf(Dart_Handle object, Dart_Handle cls); + + +// Number. +DART_EXPORT bool Dart_IsNumber(Dart_Handle object); + + +// Integer. +DART_EXPORT bool Dart_IsInteger(Dart_Handle object); +DART_EXPORT Dart_Handle Dart_NewInteger(int64_t value); +DART_EXPORT Dart_Handle Dart_NewIntegerFromHexCString(const char* value); +DART_EXPORT Dart_Result Dart_IntegerValue(Dart_Handle integer); +DART_EXPORT Dart_Result Dart_IntegerFitsIntoInt64(Dart_Handle integer); + + +// Boolean. +DART_EXPORT bool Dart_IsBoolean(Dart_Handle object); +DART_EXPORT Dart_Handle Dart_NewBoolean(bool value); +DART_EXPORT Dart_Result Dart_BooleanValue(Dart_Handle bool_object); + + +// Double. +DART_EXPORT bool Dart_IsDouble(Dart_Handle object); +DART_EXPORT Dart_Handle Dart_NewDouble(double value); +DART_EXPORT Dart_Result Dart_DoubleValue(Dart_Handle integer); + + +// String. +DART_EXPORT bool Dart_IsString(Dart_Handle object); + +DART_EXPORT Dart_Result Dart_StringLength(Dart_Handle str); + +DART_EXPORT Dart_Handle Dart_NewString(const char* str); +DART_EXPORT Dart_Handle Dart_NewString8(const uint8_t* codepoints, + intptr_t length); +DART_EXPORT Dart_Handle Dart_NewString16(const uint16_t* codepoints, + intptr_t length); +DART_EXPORT Dart_Handle Dart_NewString32(const uint32_t* codepoints, + intptr_t length); + +// The functions below test whether the object is a String and its codepoints +// all fit into 8 or 16 bits respectively. +DART_EXPORT bool Dart_IsString8(Dart_Handle object); +DART_EXPORT bool Dart_IsString16(Dart_Handle object); + +DART_EXPORT Dart_Result Dart_StringGet8(Dart_Handle str, + uint8_t* codepoints, + intptr_t length); +DART_EXPORT Dart_Result Dart_StringGet16(Dart_Handle str, + uint16_t* codepoints, + intptr_t length); +DART_EXPORT Dart_Result Dart_StringGet32(Dart_Handle str, + uint32_t* codepoints, + intptr_t length); + +DART_EXPORT Dart_Result Dart_StringToCString(Dart_Handle str); + + +// Array. +DART_EXPORT bool Dart_IsArray(Dart_Handle object); +DART_EXPORT Dart_Handle Dart_NewArray(intptr_t length); +DART_EXPORT Dart_Result Dart_GetLength(Dart_Handle array); +DART_EXPORT Dart_Result Dart_ArrayGetAt(Dart_Handle array, + intptr_t index); +DART_EXPORT Dart_Result Dart_ArrayGet(Dart_Handle array, + intptr_t offset, + uint8_t* native_array, + intptr_t length); +DART_EXPORT Dart_Result Dart_ArraySetAt(Dart_Handle array, + intptr_t index, + Dart_Handle value); +DART_EXPORT Dart_Result Dart_ArraySet(Dart_Handle array, + intptr_t offset, + uint8_t* native_array, + intptr_t length); + +// Closure. +DART_EXPORT bool Dart_IsClosure(Dart_Handle object); +// DEPRECATED: The API below is a temporary hack. +DART_EXPORT Dart_Result Dart_ClosureSmrck(Dart_Handle object); +DART_EXPORT void Dart_ClosureSetSmrck(Dart_Handle object, int64_t value); + + +// Invocation of methods. +DART_EXPORT Dart_Result Dart_InvokeStatic(Dart_Handle library, + Dart_Handle class_name, + Dart_Handle function_name, + int number_of_arguments, + Dart_Handle* arguments); +DART_EXPORT Dart_Result Dart_InvokeDynamic(Dart_Handle receiver, + Dart_Handle function_name, + int number_of_arguments, + Dart_Handle* arguments); +DART_EXPORT Dart_Result Dart_InvokeClosure(Dart_Handle closure, + int number_of_arguments, + Dart_Handle* arguments); + + +// Interaction with native methods. +DART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args, + int index); +DART_EXPORT void Dart_SetReturnValue(Dart_NativeArguments args, + Dart_Handle retval); + +// Library. +DART_EXPORT bool Dart_IsLibrary(Dart_Handle object); +DART_EXPORT Dart_Result Dart_LibraryUrl(Dart_Handle library); +DART_EXPORT Dart_Result Dart_LibraryImportLibrary(Dart_Handle library, + Dart_Handle import); + +DART_EXPORT Dart_Result Dart_LookupLibrary(Dart_Handle url); + +DART_EXPORT Dart_Result Dart_LoadLibrary(Dart_Handle url, + Dart_Handle source); +DART_EXPORT Dart_Result Dart_LoadSource(Dart_Handle library, + Dart_Handle url, + Dart_Handle source); +DART_EXPORT Dart_Result Dart_SetNativeResolver( + Dart_Handle library, + Dart_NativeEntryResolver resolver); + + +// Script handling. +DART_EXPORT Dart_Result Dart_LoadScript(Dart_Handle url, + Dart_Handle source, + Dart_LibraryTagHandler handler); + +// Compile all loaded classes and functions eagerly. +DART_EXPORT Dart_Result Dart_CompileAll(); + +// Exception related. +DART_EXPORT bool Dart_ExceptionOccurred(Dart_Handle result); +DART_EXPORT Dart_Result Dart_GetException(Dart_Handle result); +DART_EXPORT Dart_Result Dart_GetStacktrace(Dart_Handle unhandled_exception); +DART_EXPORT Dart_Result Dart_ThrowException(Dart_Handle exception); +DART_EXPORT Dart_Result Dart_ReThrowException(Dart_Handle exception, + Dart_Handle stacktrace); + +// Global Handles and Scope for local handles and zone based memory allocation. +DART_EXPORT void Dart_EnterScope(); +DART_EXPORT void Dart_ExitScope(); + +DART_EXPORT Dart_Handle Dart_NewPersistentHandle(Dart_Handle object); +DART_EXPORT Dart_Handle Dart_MakeWeakPersistentHandle(Dart_Handle object); +DART_EXPORT Dart_Handle Dart_MakePersistentHandle(Dart_Handle object); +DART_EXPORT void Dart_DeletePersistentHandle(Dart_Handle object); + +// Fields. +DART_EXPORT Dart_Result Dart_GetStaticField(Dart_Handle cls, Dart_Handle name); +DART_EXPORT Dart_Result Dart_SetStaticField(Dart_Handle cls, + Dart_Handle name, + Dart_Handle value); +DART_EXPORT Dart_Result Dart_GetInstanceField(Dart_Handle obj, + Dart_Handle name); +DART_EXPORT Dart_Result Dart_SetInstanceField(Dart_Handle obj, + Dart_Handle name, + Dart_Handle value); + +// Native fields. +DART_EXPORT Dart_Result Dart_CreateNativeWrapperClass(Dart_Handle library, + Dart_Handle class_name, + int field_count); +DART_EXPORT Dart_Result Dart_GetNativeInstanceField(Dart_Handle obj, + int index); +DART_EXPORT Dart_Result Dart_SetNativeInstanceField(Dart_Handle obj, + int index, + intptr_t value); + +// Snapshot creation. +DART_EXPORT Dart_Result Dart_CreateSnapshot(uint8_t** snaphot_buffer, + intptr_t* snapshot_size); + +// Message communication. +DART_EXPORT Dart_Result Dart_PostIntArray(Dart_Port port, + int field_count, + intptr_t* data); + +DART_EXPORT Dart_Result Dart_Post(Dart_Port port, Dart_Handle value); + +// External pprof support for gathering and dumping symbolic information +// that can be used for better profile reports for dynamically generated +// code. +DART_EXPORT void Dart_InitPprofSupport(); +DART_EXPORT void Dart_GetPprofSymbolInfo(void** buffer, int* buffer_size); + +// Check set vm flags. +DART_EXPORT bool Dart_IsVMFlagSet(const char* flag_name); + +#endif // INCLUDE_DART_API_H_ diff --git a/runtime/lib/array.cc b/runtime/lib/array.cc new file mode 100644 index 00000000000..a33ee9aeece --- /dev/null +++ b/runtime/lib/array.cc @@ -0,0 +1,142 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bootstrap_natives.h" + +#include "vm/assembler.h" +#include "vm/assert.h" +#include "vm/bigint_operations.h" +#include "vm/exceptions.h" +#include "vm/native_entry.h" +#include "vm/object.h" + +namespace dart { + +DEFINE_NATIVE_ENTRY(ObjectArray_allocate, 2) { + const TypeArguments& type_arguments = + TypeArguments::CheckedHandle(arguments->At(0)); + const Instance& length_instance = Instance::CheckedHandle(arguments->At(1)); + if (!length_instance.IsSmi()) { + GrowableArray args; + args.Add(&length_instance); + Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); + } + Smi& length = Smi::Handle(); + length ^= length_instance.raw(); + ASSERT(type_arguments.IsNull() || + (type_arguments.IsInstantiated() && (type_arguments.Length() == 1))); + if (length.IsNull() || (length.Value() < 0)) { + GrowableArray args; + args.Add(&length); + Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); + } + const Array& new_array = Array::Handle(Array::New(length.Value())); + new_array.SetTypeArguments(type_arguments); + arguments->SetReturn(new_array); +} + + +DEFINE_NATIVE_ENTRY(ObjectArray_getIndexed, 2) { + const Array& array = Array::CheckedHandle(arguments->At(0)); + const Instance& index_instance = Instance::CheckedHandle(arguments->At(1)); + if (!index_instance.IsSmi()) { + GrowableArray args; + args.Add(&index_instance); + Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); + } + Smi& index = Smi::Handle(); + index ^= index_instance.raw(); + if (array.IsNull() || index.IsNull()) { + // TODO(asiva): Need to handle error cases. + UNIMPLEMENTED(); + return; + } + if ((index.Value() < 0) || (index.Value() >= array.Length())) { + GrowableArray arguments; + arguments.Add(&index); + Exceptions::ThrowByType(Exceptions::kIndexOutOfRange, arguments); + } + const Instance& obj = Instance::CheckedHandle(array.At(index.Value())); + arguments->SetReturn(obj); +} + + +DEFINE_NATIVE_ENTRY(ObjectArray_setIndexed, 3) { + const Array& array = Array::CheckedHandle(arguments->At(0)); + const Smi& index = Smi::CheckedHandle(arguments->At(1)); + const Instance& value = Instance::CheckedHandle(arguments->At(2)); + if (array.IsNull() || index.IsNull()) { + // TODO(asiva): Need to handle error cases. + UNIMPLEMENTED(); + return; + } + if ((index.Value() < 0) || (index.Value() >= array.Length())) { + GrowableArray arguments; + arguments.Add(&index); + Exceptions::ThrowByType(Exceptions::kIndexOutOfRange, arguments); + } + array.SetAt(index.Value(), value); +} + + +DEFINE_NATIVE_ENTRY(ObjectArray_getLength, 1) { + const Array& array = Array::CheckedHandle(arguments->At(0)); + if (array.IsNull()) { + // TODO(asiva): Need to handle error cases. + UNIMPLEMENTED(); + return; + } + const Smi& length = Smi::Handle(Smi::New(array.Length())); + arguments->SetReturn(length); +} + + +// ObjectArray src, int srcStart, int dstStart, int count. +DEFINE_NATIVE_ENTRY(ObjectArray_copyFromObjectArray, 5) { + const Array& dest = Array::CheckedHandle(arguments->At(0)); + const Array& source = Array::CheckedHandle(arguments->At(1)); + const Smi& src_start = Smi::CheckedHandle(arguments->At(2)); + const Smi& dst_start = Smi::CheckedHandle(arguments->At(3)); + const Smi& count = Smi::CheckedHandle(arguments->At(4)); + if (dest.IsNull() || source.IsNull() || src_start.IsNull() || + dst_start.IsNull() || count.IsNull()) { + GrowableArray args; + Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); + } + intptr_t icount = count.Value(); + if (icount < 0) { + GrowableArray args; + Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); + } + if (icount == 0) { + return; + } + intptr_t isrc_start = src_start.Value(); + intptr_t idst_start = dst_start.Value(); + if ((isrc_start + icount) > source.Length()) { + GrowableArray arguments; + arguments.Add(&src_start); + Exceptions::ThrowByType(Exceptions::kIndexOutOfRange, arguments); + } + if ((idst_start + icount) > dest.Length()) { + GrowableArray arguments; + arguments.Add(&dst_start); + Exceptions::ThrowByType(Exceptions::kIndexOutOfRange, arguments); + } + + Object& src_obj = Object::Handle(); + if (isrc_start < idst_start) { + for (intptr_t i = icount - 1; i >= 0; i--) { + src_obj = source.At(isrc_start + i); + dest.SetAt(idst_start + i, src_obj); + } + } else { + for (intptr_t i = 0; i < icount; i++) { + src_obj = source.At(isrc_start + i); + dest.SetAt(idst_start + i, src_obj); + } + } +} + +} // namespace dart diff --git a/runtime/lib/array.dart b/runtime/lib/array.dart new file mode 100644 index 00000000000..59a076f3b89 --- /dev/null +++ b/runtime/lib/array.dart @@ -0,0 +1,294 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class ArrayFactory { + factory Array.from(Iterable other) { + GrowableObjectArray array = new GrowableObjectArray(); + for (final e in other) { + array.add(e); + } + return array; + } + + factory Array.fromArray(Array other, int startIndex, int endIndex) { + Array array = new Array(); + if (endIndex > other.length) endIndex = other.length; + if (startIndex < 0) startIndex = 0; + int count = endIndex - startIndex; + if (count > 0) { + array.length = count; + Arrays.copy(other, startIndex, array, 0, count); + } + return array; + } + + factory Array([int length = null]) { + if (length === null) { + return new GrowableObjectArray(); + } else { + return new ObjectArray(length); + } + } +} + +class ListFactory { + + factory List.from(Iterable other) { + GrowableObjectArray list = new GrowableObjectArray(); + for (final e in other) { + list.add(e); + } + return list; + } + + factory List.fromList(List other, int startIndex, int endIndex) { + List list = new List(); + if (endIndex > other.length) endIndex = other.length; + if (startIndex < 0) startIndex = 0; + int count = endIndex - startIndex; + if (count > 0) { + list.length = count; + Arrays.copy(other, startIndex, list, 0, count); + } + return list; + } + + factory List([int length = null]) { + if (length === null) { + return new GrowableObjectArray(); + } else { + return new ObjectArray(length); + } + } +} + +// TODO(srdjan): Use shared array implementation. +class ObjectArray implements Array { + + factory ObjectArray(int length) native "ObjectArray_allocate"; + + T operator [](int index) native "ObjectArray_getIndexed"; + + void operator []=(int index, T value) native "ObjectArray_setIndexed"; + + String toString() { + return Arrays.asString(this); + } + + int get length() native "ObjectArray_getLength"; + + void copyFrom(Array src, int srcStart, int dstStart, int count) { + if (src is ObjectArray) { + _copyFromObjectArray(src, srcStart, dstStart, count); + } else { + Arrays.copy(src, srcStart, this, dstStart, count); + } + } + + void _copyFromObjectArray(ObjectArray src, + int srcStart, + int dstStart, + int count) + native "ObjectArray_copyFromObjectArray"; + + /** + * Collection interface. + */ + + void forEach(f(T element)) { + Collections.forEach(this, f); + } + + Collection filter(bool f(T element)) { + return Collections.filter(this, new GrowableObjectArray(), f); + } + + bool every(bool f(T element)) { + return Collections.every(this, f); + } + + bool some(bool f(T element)) { + return Collections.some(this, f); + } + + bool isEmpty() { + return this.length === 0; + } + + void sort(int compare(T a, T b)) { + DualPivotQuicksort.sort(this, compare); + } + + int indexOf(T element, int startIndex) { + return Arrays.indexOf(this, element, startIndex, this.length); + } + + int lastIndexOf(T element, int startIndex) { + return Arrays.lastIndexOf(this, element, startIndex); + } + + Iterator iterator() { + return new FixedSizeArrayIterator(this); + } + + void add(T element) { + throw const UnsupportedOperationException( + "Cannot add to a non-extendable array"); + } + + void addLast(T element) { + add(element); + } + + void addAll(Collection elements) { + throw const UnsupportedOperationException( + "Cannot add to a non-extendable array"); + } + + void clear() { + throw const UnsupportedOperationException( + "Cannot clear a non-extendable array"); + } + + void set length(int length) { + throw const UnsupportedOperationException( + "Cannot change the length of a non-extendable array"); + } + + T removeLast() { + throw const UnsupportedOperationException( + "Cannot remove in a non-extendable array"); + } + + T last() { + return this[length - 1]; + } +} + + +// This is essentially the same class as ObjectArray, but it does not +// permit any modification of array elements from Dart code. We use +// this class for arrays constructed from Dart array literals. +// TODO(hausner): We should consider the trade-offs between two +// classes (and inline cache misses) versus a field in the native +// implementation (checks when modifying). We should keep watching +// the inline cache misses. +class ImmutableArray implements Array { + + T operator [](int index) native "ObjectArray_getIndexed"; + + void operator []=(int index, T value) { + throw const UnsupportedOperationException( + "Cannot modify an immutable array"); + } + + int get length() native "ObjectArray_getLength"; + + void copyFrom(Array src, int srcStart, int dstStart, int count) { + throw const UnsupportedOperationException( + "Cannot modify an immutable array"); + } + + /** + * Collection interface. + */ + + void forEach(f(T element)) { + Collections.forEach(this, f); + } + + Collection filter(bool f(T element)) { + return Collections.filter(this, new GrowableObjectArray(), f); + } + + bool every(bool f(T element)) { + return Collections.every(this, f); + } + + bool some(bool f(T element)) { + return Collections.some(this, f); + } + + bool isEmpty() { + return this.length === 0; + } + + void sort(int compare(T a, T b)) { + throw const UnsupportedOperationException( + "Cannot modify an immutable array"); + } + + String toString() { + return "ImmutableArray"; + } + + int indexOf(T element, int startIndex) { + return Arrays.indexOf(this, element, startIndex, this.length); + } + + int lastIndexOf(T element, int startIndex) { + return Arrays.lastIndexOf(this, element, startIndex); + } + + Iterator iterator() { + return new FixedSizeArrayIterator(this); + } + + void add(T element) { + throw const UnsupportedOperationException( + "Cannot add to an immutable array"); + } + + void addLast(T element) { + add(element); + } + + void addAll(Collection elements) { + throw const UnsupportedOperationException( + "Cannot add to an immutable array"); + } + + void clear() { + throw const UnsupportedOperationException( + "Cannot clear an immutable array"); + } + + void set length(int length) { + throw const UnsupportedOperationException( + "Cannot change the length of an immutable array"); + } + + T removeLast() { + throw const UnsupportedOperationException( + "Cannot remove in a non-extendable array"); + } + + T last() { + return this[length - 1]; + } +} + + +// Iterator for arrays with fixed size. +class FixedSizeArrayIterator implements Iterator { + FixedSizeArrayIterator(Array array) + : _array = array, _length = array.length, _pos = 0 { + assert(array is ObjectArray || array is ImmutableArray); + } + + bool hasNext() { + return _length > _pos; + } + + T next() { + if (!hasNext()) { + throw const NoMoreElementsException(); + } + return _array[_pos++]; + } + + final Array _array; + final int _length; // Cache array length for faster access. + int _pos; +} diff --git a/runtime/lib/arrays.dart b/runtime/lib/arrays.dart new file mode 100644 index 00000000000..af39a05666e --- /dev/null +++ b/runtime/lib/arrays.dart @@ -0,0 +1,90 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class Arrays { + + static String asString(Array array) { + String result = "["; + int len = array.length; + for (int i = 0; i < len; i++) { + // TODO(4466785): Deal with recursion and formatting. + result += array[i].toString() + ", "; + } + result += "]"; + return result; + } + + static void copy(Array src, int srcStart, + Array dst, int dstStart, int count) { + if (srcStart === null) srcStart = 0; + if (dstStart === null) dstStart = 0; + + if (srcStart < dstStart) { + for (int i = srcStart + count - 1, j = dstStart + count - 1; + i >= srcStart; i--, j--) { + dst[j] = src[i]; + } + } else { + for (int i = srcStart, j = dstStart; i < srcStart + count; i++, j++) { + dst[j] = src[i]; + } + } + } + + static bool areEqual(Array a, Object b) { + if (a === b) return true; + if (!(b is Array)) return false; + int length = a.length; + if (length != b.length) return false; + + for (int i = 0; i < length; i++) { + if (a[i] !== b[i]) return false; + } + return true; + } + + /** + * Returns the index in the array [a] of the given [element], starting + * the search at index [startIndex] to [endIndex] (exclusive). + * Returns -1 if [element] is not found. + */ + static int indexOf(Array a, + Object element, + int startIndex, + int endIndex) { + if (startIndex >= a.length) { + return -1; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < endIndex; i++) { + if (a[i] == element) { + return i; + } + } + return -1; + } + + /** + * Returns the last index in the array [a] of the given [element], starting + * the search at index [startIndex] to 0. + * Returns -1 if [element] is not found. + */ + static int lastIndexOf(Array a, Object element, int startIndex) { + if (startIndex < 0) { + return -1; + } + if (startIndex >= a.length) { + startIndex = a.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (a[i] == element) { + return i; + } + } + return -1; + } +} + diff --git a/runtime/lib/bool.dart b/runtime/lib/bool.dart new file mode 100644 index 00000000000..084a888bc88 --- /dev/null +++ b/runtime/lib/bool.dart @@ -0,0 +1,6 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class Bool implements bool { +} diff --git a/runtime/lib/clock.cc b/runtime/lib/clock.cc new file mode 100644 index 00000000000..1a07623c4c9 --- /dev/null +++ b/runtime/lib/clock.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bootstrap_natives.h" + +#include "vm/object.h" +#include "vm/os.h" + +namespace dart { + +DEFINE_NATIVE_ENTRY(Clock_now, 0) { + // TODO(iposva): investigate other hi-res time sources such as cycle count. + const Integer& micros = + Integer::Handle(Integer::New(OS::GetCurrentTimeMicros())); + arguments->SetReturn(micros); +} + + +DEFINE_NATIVE_ENTRY(Clock_frequency, 0) { + // TODO(iposva): investigate other hi-res time sources such as cycle count. + const Integer& frequency = Integer::Handle(Integer::New(1000000)); + arguments->SetReturn(frequency); +} + +} // namespace dart diff --git a/runtime/lib/clock.dart b/runtime/lib/clock.dart new file mode 100644 index 00000000000..2dc2f330396 --- /dev/null +++ b/runtime/lib/clock.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/** + * The class [Clock] provides access to a monotonically incrementing clock + * device. + */ +class Clock { + + /** + * Returns the current clock tick. + */ + static int now() native "Clock_now"; + + /** + * Returns the frequency of clock ticks in Hz. + */ + static int frequency() native "Clock_frequency"; + +} diff --git a/runtime/lib/collections.dart b/runtime/lib/collections.dart new file mode 100644 index 00000000000..872e0378c98 --- /dev/null +++ b/runtime/lib/collections.dart @@ -0,0 +1,43 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/** + * The [Collections] class implements static methods useful when + * writing a class that implements [Collection] and the [iterator] + * method. + */ +class Collections { + static void forEach(Iterable iterable, void f(Object o)) { + for (final e in iterable) { + f(e); + } + } + + static bool some(Iterable iterable, bool f(Object o)) { + for (final e in iterable) { + if (f(e)) return true; + } + return false; + } + + static bool every(Iterable iterable, bool f(Object o)) { + for (final e in iterable) { + if (!f(e)) return false; + } + return true; + } + + static List filter(Iterable source, + List destination, + bool f(Object o)) { + for (final e in source) { + if (f(e)) destination.add(e); + } + return destination; + } + + static bool isEmpty(Iterable iterable) { + return !iterable.iterator().hasNext(); + } +} diff --git a/runtime/lib/date_time.cc b/runtime/lib/date_time.cc new file mode 100644 index 00000000000..a719f9b6182 --- /dev/null +++ b/runtime/lib/date_time.cc @@ -0,0 +1,155 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bootstrap_natives.h" + +#include "vm/bigint_operations.h" +#include "vm/native_entry.h" +#include "vm/object.h" +#include "vm/os.h" + +namespace dart { + +static bool BreakDownSecondsSinceEpoch(const Integer& dart_seconds, + const Bool& dart_is_utc, + OS::BrokenDownDateTime* result) { + bool is_utc = dart_is_utc.value(); + int64_t value = dart_seconds.AsInt64Value(); + time_t seconds = static_cast(value); + return OS::BreakDownSecondsSinceEpoch(seconds, is_utc, result); +} + + +DEFINE_NATIVE_ENTRY(DateTimeNatives_brokenDownToSecondsSinceEpoch, 7) { + const Integer& dart_years = Integer::CheckedHandle(arguments->At(0)); + const Smi& dart_month = Smi::CheckedHandle(arguments->At(1)); + const Smi& dart_day = Smi::CheckedHandle(arguments->At(2)); + const Smi& dart_hours = Smi::CheckedHandle(arguments->At(3)); + const Smi& dart_minutes = Smi::CheckedHandle(arguments->At(4)); + const Smi& dart_seconds = Smi::CheckedHandle(arguments->At(5)); + const Bool& dart_is_utc = Bool::CheckedHandle(arguments->At(6)); + if (!dart_years.IsSmi()) { + UNIMPLEMENTED(); + } + Smi& smi_years = Smi::Handle(); + smi_years ^= dart_years.raw(); + OS::BrokenDownDateTime broken_down; + // mktime takes the years since 1900. + // TODO(floitsch): Removing 1900 could underflow the intptr_t. + intptr_t year = smi_years.Value() - 1900; + // TODO(floitsch): We don't handle the case yet where intptr_t and int have + // different sizes. + ASSERT(sizeof(year) <= sizeof(broken_down.year)); + broken_down.year = year; + // libc months are 0-based (contrary to Dart' 1-based months). + broken_down.month = dart_month.Value() - 1; + broken_down.day = dart_day.Value(); + broken_down.hours = dart_hours.Value(); + broken_down.minutes = dart_minutes.Value(); + broken_down.seconds = dart_seconds.Value(); + time_t value; + bool succeeded = OS::BrokenDownToSecondsSinceEpoch(broken_down, + dart_is_utc.value(), + &value); + if (!succeeded) { + UNIMPLEMENTED(); + } + arguments->SetReturn(Integer::Handle(Integer::New(value))); +} + + +DEFINE_NATIVE_ENTRY(DateTimeNatives_currentTimeMillis, 0) { + const Integer& time = Integer::Handle( + Integer::New(OS::GetCurrentTimeMillis())); + arguments->SetReturn(time); +} + + +DEFINE_NATIVE_ENTRY(DateTimeNatives_getYear, 2) { + const Integer& dart_seconds = Integer::CheckedHandle(arguments->At(0)); + const Bool& dart_is_utc = Bool::CheckedHandle(arguments->At(1)); + OS::BrokenDownDateTime broken_down; + bool succeeded = + BreakDownSecondsSinceEpoch(dart_seconds, dart_is_utc, &broken_down); + if (!succeeded) { + UNIMPLEMENTED(); + } + // C uses years since 1900, and not full years. + // TODO(floitsch): adding 1900 could overflow the intptr_t. + intptr_t year = broken_down.year + 1900; + arguments->SetReturn(Integer::Handle(Integer::New(year))); +} + + +DEFINE_NATIVE_ENTRY(DateTimeNatives_getMonth, 2) { + const Integer& dart_seconds = Integer::CheckedHandle(arguments->At(0)); + const Bool& dart_is_utc = Bool::CheckedHandle(arguments->At(1)); + OS::BrokenDownDateTime broken_down; + bool succeeded = + BreakDownSecondsSinceEpoch(dart_seconds, dart_is_utc, &broken_down); + if (!succeeded) { + UNIMPLEMENTED(); + } + // Dart has 1-based months (contrary to C's 0-based). + const Smi& result = Smi::Handle(Smi::New(broken_down.month + 1)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(DateTimeNatives_getDay, 2) { + const Integer& dart_seconds = Integer::CheckedHandle(arguments->At(0)); + const Bool& dart_is_utc = Bool::CheckedHandle(arguments->At(1)); + OS::BrokenDownDateTime broken_down; + bool succeeded = + BreakDownSecondsSinceEpoch(dart_seconds, dart_is_utc, &broken_down); + if (!succeeded) { + UNIMPLEMENTED(); + } + const Smi& result = Smi::Handle(Smi::New(broken_down.day)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(DateTimeNatives_getHours, 2) { + const Integer& dart_seconds = Integer::CheckedHandle(arguments->At(0)); + const Bool& dart_is_utc = Bool::CheckedHandle(arguments->At(1)); + OS::BrokenDownDateTime broken_down; + bool succeeded = + BreakDownSecondsSinceEpoch(dart_seconds, dart_is_utc, &broken_down); + if (!succeeded) { + UNIMPLEMENTED(); + } + const Smi& result = Smi::Handle(Smi::New(broken_down.hours)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(DateTimeNatives_getMinutes, 2) { + const Integer& dart_seconds = Integer::CheckedHandle(arguments->At(0)); + const Bool& dart_is_utc = Bool::CheckedHandle(arguments->At(1)); + OS::BrokenDownDateTime broken_down; + bool succeeded = + BreakDownSecondsSinceEpoch(dart_seconds, dart_is_utc, &broken_down); + if (!succeeded) { + UNIMPLEMENTED(); + } + const Smi& result = Smi::Handle(Smi::New(broken_down.minutes)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(DateTimeNatives_getSeconds, 2) { + const Integer& dart_seconds = Integer::CheckedHandle(arguments->At(0)); + const Bool& dart_is_utc = Bool::CheckedHandle(arguments->At(1)); + OS::BrokenDownDateTime broken_down; + bool succeeded = + BreakDownSecondsSinceEpoch(dart_seconds, dart_is_utc, &broken_down); + if (!succeeded) { + UNIMPLEMENTED(); + } + const Smi& result = Smi::Handle(Smi::New(broken_down.seconds)); + arguments->SetReturn(result); +} + +} // namespace dart diff --git a/runtime/lib/date_time.dart b/runtime/lib/date_time.dart new file mode 100644 index 00000000000..97d4e837048 --- /dev/null +++ b/runtime/lib/date_time.dart @@ -0,0 +1,424 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Dart core library. + +class TimeZoneImplementation implements TimeZone { + Time get offset() { + if (isUtc) return const Time.duration(0); + throw "Unimplemented"; + } + factory TimeZoneImplementation(Time offset) { + if (offset.duration == 0) { + return const TimeZoneImplementation.utc(); + } else { + throw "Unimplemented"; + } + } + + const TimeZoneImplementation.utc() : isUtc = true; + TimeZoneImplementation.local() : isUtc = false {} + + bool operator ==(Object other) { + if (!(other is TimeZoneImplementation)) return false; + return isUtc == other.isUtc; + } + + final bool isUtc; +} + +// JavaScript implementation of DateTimeImplementation. +class DateTimeImplementation implements DateTime { + factory DateTimeImplementation(int years, + int month, + int day, + int hours, + int minutes, + int seconds, + int milliseconds) { + return new DateTimeImplementation.withTimeZone( + years, month, day, + hours, minutes, seconds, milliseconds, + new TimeZoneImplementation.local()); + } + + DateTimeImplementation.withTimeZone(int years, + int month, + int day, + int hours, + int minutes, + int seconds, + int milliseconds, + TimeZoneImplementation timeZone) + : timeZone = timeZone, + value = brokenDownDateTimeToMillisecondsSinceEpoch_( + years, month, day, hours, minutes, seconds, milliseconds, + timeZone.isUtc) {} + + factory DateTimeImplementation.fromDateAndTime( + Date date, + Time time, + TimeZoneImplementation timeZone) { + if (timeZone === null) { + timeZone = new TimeZoneImplementation.local(); + } + return new DateTimeImplementation.withTimeZone(date.year, + date.month, + date.day + time.days, + time.hours, + time.minutes, + time.seconds, + time.milliseconds, + timeZone); + } + + DateTimeImplementation.now() + : timeZone = new TimeZone.local(), + value = getCurrentMs_() { + } + + factory DateTimeImplementation.fromString(String formattedString) { + int substringToNumber(String str, int from, int to) { + int result = 0; + for (int i = from; i < to; i++) { + result = result * 10 + str.charCodeAt(i) - "0".charCodeAt(0); + } + return result; + } + + // TODO(floitsch): improve DateTimeImplementation parsing. + // Parse ISO 8601: "2011-05-14 00:37:18.231Z". + int yearMonthSeparator = formattedString.indexOf("-", 0); + if (yearMonthSeparator < 0) throw "UNIMPLEMENTED"; + int monthDaySeparator = + formattedString.indexOf("-", yearMonthSeparator + 1); + if (monthDaySeparator < 0) throw "UNIMPLEMENTED"; + int dateTimeSeparator = formattedString.indexOf(" ", monthDaySeparator + 1); + if (dateTimeSeparator < 0) throw "UNIMPLEMENTED"; + int hoursMinutesSeparator = + formattedString.indexOf(":", dateTimeSeparator + 1); + if (hoursMinutesSeparator < 0) throw "UNIMPLEMENTED"; + int minutesSecondsSeparator = + formattedString.indexOf(":", hoursMinutesSeparator + 1); + if (minutesSecondsSeparator < 0) throw "UNIMPLEMENTED"; + int secondsMillisecondsSeparator = + formattedString.indexOf(".", minutesSecondsSeparator + 1); + bool isUtc = formattedString.endsWith("Z"); + int end = formattedString.length - (isUtc ? 1 : 0); + if (secondsMillisecondsSeparator < 0) secondsMillisecondsSeparator = end; + + int year = substringToNumber(formattedString, 0, yearMonthSeparator); + int month = substringToNumber(formattedString, + yearMonthSeparator + 1, + monthDaySeparator); + int day = substringToNumber(formattedString, + monthDaySeparator + 1, + dateTimeSeparator); + int hours = substringToNumber(formattedString, + dateTimeSeparator + 1, + hoursMinutesSeparator); + int minutes = substringToNumber(formattedString, + hoursMinutesSeparator + 1, + minutesSecondsSeparator); + int seconds = substringToNumber(formattedString, + minutesSecondsSeparator + 1, + secondsMillisecondsSeparator); + int milliseconds = substringToNumber(formattedString, + secondsMillisecondsSeparator + 1, + end); + TimeZone timeZone = (isUtc ? const TimeZone.utc() : new TimeZone.local()); + return new DateTimeImplementation.withTimeZone( + year, month, day, hours, minutes, seconds, milliseconds, timeZone); + } + + const DateTimeImplementation.fromEpoch(int this.value, + TimeZone this.timeZone); + + bool operator ==(Object other) { + if (!(other is DateTimeImplementation)) return false; + return value == other.value && timeZone == other.timeZone; + } + + int compareTo(DateTime other) { + return value.compareTo(other.value); + } + + DateTime changeTimeZone(TimeZone targetTimeZone) { + if (targetTimeZone === null) { + targetTimeZone = new TimeZoneImplementation.local(); + } + return new DateTime.fromEpoch(value, targetTimeZone); + } + + Date get date() { + return new DateImplementation(year, month, day); + } + + Time get time() { + return new TimeImplementation(0, hours, minutes, seconds, milliseconds); + } + + int get year() { + int secondsSinceEpoch = secondsSinceEpoch_; + // According to V8 some library calls have troubles with negative values. + // Therefore clamp to 0 - year 2035 (which is less than the size of 32bit). + if (secondsSinceEpoch >= 0 && secondsSinceEpoch < SECONDS_YEAR_2035_) { + return getYear_(secondsSinceEpoch, timeZone.isUtc); + } + + // Approximate the result. We don't take timeZone into account. + int approximateYear = yearsFromSecondsSinceEpoch_(secondsSinceEpoch); + int equivalentYear = equivalentYear_(approximateYear); + int y = getYear_(equivalentSeconds_(secondsSinceEpoch_), timeZone.isUtc); + return approximateYear + (y - equivalentYear); + } + + int get month() { + return getMonth_(equivalentSeconds_(secondsSinceEpoch_), timeZone.isUtc); + } + + int get day() { + return getDay_(equivalentSeconds_(secondsSinceEpoch_), timeZone.isUtc); + } + + int get hours() { + return getHours_(equivalentSeconds_(secondsSinceEpoch_), timeZone.isUtc); + } + + int get minutes() { + return getMinutes_(equivalentSeconds_(secondsSinceEpoch_), timeZone.isUtc); + } + + int get seconds() { + return getSeconds_(equivalentSeconds_(secondsSinceEpoch_), timeZone.isUtc); + } + + int get milliseconds() { + return value % Time.MS_PER_SECOND; + } + + int get secondsSinceEpoch_() { + // Always round down. + if (value < 0) { + return (value + 1) ~/ Time.MS_PER_SECOND - 1; + } else { + return value ~/ Time.MS_PER_SECOND; + } + } + + int get weekday() { + throw "Unimplemented"; + } + + bool isLocalTime() { + return !timeZone.isUtc; + } + + bool isUtc() { + return timeZone.isUtc; + } + + String toString() { + // TODO(floitsch): switch to string-interpolation. + if (timeZone.isUtc) { + return date.toString() + " " + time.toString() + "Z"; + } else { + return date.toString() + " " + time.toString(); + } + } + + // Adds the duration [time] to this DateTime instance. + DateTime add(Time time) { + return new DateTimeImplementation.fromEpoch(value + time.duration, + timeZone); + } + + // Subtracts the duration [time] from this DateTime instance. + DateTime subtract(Time time) { + return new DateTimeImplementation.fromEpoch(value - time.duration, + timeZone); + } + + // Returns a [Time] with the difference of [this] and [other]. + Time difference(DateTime other) { + return new TimeImplementation.duration(value - other.value); + } + + final int value; + final TimeZoneImplementation timeZone; + + static final int SECONDS_YEAR_2035_ = 2051222400; + + // Returns the UTC year for the corresponding [secondsSinceEpoch]. + // It is relatively fast for values in the range 0 to year 2098. + // Code is adapted from V8. + static int yearsFromSecondsSinceEpoch_(int secondsSinceEpoch) { + final int DAYS_IN_4_YEARS = 4 * 365 + 1; + final int DAYS_IN_100_YEARS = 25 * DAYS_IN_4_YEARS - 1; + final int DAYS_IN_400_YEARS = 4 * DAYS_IN_100_YEARS + 1; + final int DAYS_1970_TO_2000 = 30 * 365 + 7; + final int DAYS_OFFSET = 1000 * DAYS_IN_400_YEARS + 5 * DAYS_IN_400_YEARS - + DAYS_1970_TO_2000; + final int YEARS_OFFSET = 400000; + final int DAYS_YEAR_2098 = DAYS_IN_100_YEARS + 6 * DAYS_IN_4_YEARS; + + int days = secondsSinceEpoch ~/ Time.SECONDS_PER_DAY; + if (days > 0 && days < DAYS_YEAR_2098) { + // According to V8 this fast case works for dates from 1970 to 2099. + return 1970 + (4 * days + 2) ~/ DAYS_IN_4_YEARS; + } else { + days += DAYS_OFFSET; + int result = 400 * (days ~/ DAYS_IN_400_YEARS) - YEARS_OFFSET; + days = days.remainder(DAYS_IN_400_YEARS); + days--; + int yd1 = days ~/ DAYS_IN_100_YEARS; + days = days.remainder(DAYS_IN_100_YEARS); + result += 100 * yd1; + days++; + int yd2 = days ~/ DAYS_IN_4_YEARS; + days = days.remainder(DAYS_IN_4_YEARS); + result += 4 * yd2; + days--; + int yd3 = days ~/ 365; + days = days.remainder(365); + result += yd3; + return result; + } + } + + // Given [secondsSinceEpoch] returns seconds such that they are at the same + // time in an equivalent year (see [equivalentYear_]). + // Leap seconds are ignored. + static int equivalentSeconds_(int secondsSinceEpoch) { + if (secondsSinceEpoch >= 0 && secondsSinceEpoch < SECONDS_YEAR_2035_) { + return secondsSinceEpoch; + } + int year = yearsFromSecondsSinceEpoch_(secondsSinceEpoch); + int days = dayFromYear_(year); + int equivalentYear = equivalentYear_(year); + int equivalentDays = dayFromYear_(equivalentYear); + int diffDays = equivalentDays - days; + return secondsSinceEpoch + diffDays * Time.SECONDS_PER_DAY; + } + + // Returns the days since 1970 for the start of the given [year]. + // [year] may be before epoch. + static int dayFromYear_(int year) { + int flooredDivision(int a, int b) { + return (a - (a < 0 ? b - 1 : 0)) ~/ b; + } + + return 365 * (year - 1970) + + flooredDivision(year - 1969, 4) + - flooredDivision(year - 1901, 100) + + flooredDivision(year - 1601, 400); + } + + // Returns a year in the range 2008-2035 matching + // - leap year, and + // - week day of first day. + // Leap seconds are ignored. + // Adapted from V8's date implementation. See ECMA 262 - 15.9.1.9. + static equivalentYear_(int year) { + // Returns 1 if in leap year. 0 otherwise. + bool inLeapYear(year) { + return (year.remainder(4) == 0) && + ((year.remainder(100) != 0) || (year.remainder(400) == 0)); + } + + // Returns the week day (in range 0 - 6). + int weekDay(year) { + // 1/1/1970 was a Thursday. + return (dayFromYear_(year) + 4) % 7; + } + // 1/1/1956 was a Sunday (i.e. weekday 0). 1956 was a leap-year. + // 1/1/1967 was a Sunday (i.e. weekday 0). + // Without leap years a subsequent year has a week day + 1 (for example + // 1/1/1968 was a Monday). With leap-years it jumps over one week day + // (e.g. 1/1/1957 was a Tuesday). + // After 12 years the weekdays have advanced by 12 days + 3 leap days = + // 15 days. 15 % 7 = 1. So after 12 years the week day has always + // (now independently of leap-years) advanced by one. + // weekDay * 12 gives thus a year starting with the wanted weekDay. + int recentYear = (inLeapYear(year) ? 1956 : 1967) + (weekDay(year) * 12); + // Close to the year 2008 the calendar cycles every 4 * 7 years (4 for the + // leap years, 7 for the weekdays). + // Find the year in the range 2008..2037 that is equivalent mod 28. + return 2008 + (recentYear - 2008) % 28; + } + + static brokenDownDateTimeToMillisecondsSinceEpoch_( + int years, int month, int day, + int hours, int minutes, int seconds, int milliseconds, + bool isUtc) { + if ((month < 1) || (month > 12)) { + throw new IllegalArgumentException(); + } + if ((day < 1) || (day > 31)) { + throw new IllegalArgumentException(); + } + // Leap seconds can lead to hours == 24. + if ((hours < 0) || (hours > 24)) { + throw new IllegalArgumentException(); + } + if ((hours == 24) && ((minutes != 0) || (seconds != 0))) { + throw new IllegalArgumentException(); + } + if ((minutes < 0) || (minutes > 59)) { + throw new IllegalArgumentException(); + } + if ((seconds < 0) || (seconds > 59)) { + throw new IllegalArgumentException(); + } + if ((milliseconds < 0) || (milliseconds > 999)) { + throw new IllegalArgumentException(); + } + int equivalentYear; + int offsetInSeconds; + // According to V8 some library calls have troubles with negative values. + // Therefore clamp to 1970 - year 2035 (which is less than the size of + // 32bit). + // We exclude the year 1970 when the time is not UTC, since the epoch + // value could then be negative. + if (years < (isUtc ? 1970 : 1971) || years > 2035) { + equivalentYear = equivalentYear_(years); + int offsetInDays = (dayFromYear_(years) - dayFromYear_(equivalentYear)); + // Leap seconds are ignored. + offsetInSeconds = offsetInDays * Time.SECONDS_PER_DAY; + } else { + equivalentYear = years; + offsetInSeconds = 0; + } + int secondsSinceEpoch = brokenDownDateTimeToSecondsSinceEpoch_( + equivalentYear, month, day, hours, minutes, seconds, isUtc); + int adjustedSeconds = secondsSinceEpoch + offsetInSeconds; + return adjustedSeconds * Time.MS_PER_SECOND + milliseconds; + } + + // Natives + static brokenDownDateTimeToSecondsSinceEpoch_( + int years, int month, int day, int hours, int minutes, int seconds, + bool isUtc) native "DateTimeNatives_brokenDownToSecondsSinceEpoch"; + + static int getCurrentMs_() native "DateTimeNatives_currentTimeMillis"; + + // TODO(floitsch): it would be more efficient if we didn't call the native + // function for every member, but cached the broken-down date. + static int getYear_(int secondsSinceEpoch, bool isUtc) + native "DateTimeNatives_getYear"; + + static int getMonth_(int secondsSinceEpoch, bool isUtc) + native "DateTimeNatives_getMonth"; + + static int getDay_(int secondsSinceEpoch, bool isUtc) + native "DateTimeNatives_getDay"; + + static int getHours_(int secondsSinceEpoch, bool isUtc) + native "DateTimeNatives_getHours"; + + static int getMinutes_(int secondsSinceEpoch, bool isUtc) + native "DateTimeNatives_getMinutes"; + + static int getSeconds_(int secondsSinceEpoch, bool isUtc) + native "DateTimeNatives_getSeconds"; +} diff --git a/runtime/lib/double.cc b/runtime/lib/double.cc new file mode 100644 index 00000000000..74e90bfe2e7 --- /dev/null +++ b/runtime/lib/double.cc @@ -0,0 +1,212 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include + +#include "vm/bootstrap_natives.h" + +#include "vm/bigint_operations.h" +#include "vm/exceptions.h" +#include "vm/native_entry.h" +#include "vm/object.h" + +namespace dart { + +DEFINE_NATIVE_ENTRY(Double_doubleFromInteger, 2) { + ASSERT(TypeArguments::CheckedHandle(arguments->At(0)).IsNull()); + const Integer& value = Integer::CheckedHandle(arguments->At(1)); + const Double& result = Double::Handle(Double::New(value.AsDoubleValue())); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Double_add, 2) { + double left = Double::CheckedHandle(arguments->At(0)).value(); + double right = Double::CheckedHandle(arguments->At(1)).value(); + const Double& result = Double::Handle(Double::New(left + right)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Double_sub, 2) { + double left = Double::CheckedHandle(arguments->At(0)).value(); + double right = Double::CheckedHandle(arguments->At(1)).value(); + const Double& result = Double::Handle(Double::New(left - right)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Double_mul, 2) { + double left = Double::CheckedHandle(arguments->At(0)).value(); + double right = Double::CheckedHandle(arguments->At(1)).value(); + const Double& result = Double::Handle(Double::New(left * right)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Double_div, 2) { + double left = Double::CheckedHandle(arguments->At(0)).value(); + double right = Double::CheckedHandle(arguments->At(1)).value(); + const Double& result = Double::Handle(Double::New(left / right)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Double_trunc_div, 2) { + double left = Double::CheckedHandle(arguments->At(0)).value(); + double right = Double::CheckedHandle(arguments->At(1)).value(); + const Double& result = Double::Handle(Double::New(trunc(left / right))); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Double_modulo, 2) { + double left = Double::CheckedHandle(arguments->At(0)).value(); + double right = Double::CheckedHandle(arguments->At(1)).value(); + double remainder = fmod(left, right); + if (remainder == 0.0) { + // We explicitely switch to the positive 0.0 (just in case it was negative). + remainder = +0.0; + } else if (remainder < 0) { + if (right < 0) { + remainder -= right; + } else { + remainder += right; + } + } + const Double& result = Double::Handle(Double::New(remainder)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Double_remainder, 2) { + double left = Double::CheckedHandle(arguments->At(0)).value(); + double right = Double::CheckedHandle(arguments->At(1)).value(); + const Double& result = Double::Handle(Double::New(fmod(left, right))); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Double_greaterThan, 2) { + const Double& left = Double::CheckedHandle(arguments->At(0)); + const Double& right = Double::CheckedHandle(arguments->At(1)); + bool result = right.IsNull() ? false : (left.value() > right.value()); + arguments->SetReturn(Bool::Handle(Bool::Get(result))); +} + + +DEFINE_NATIVE_ENTRY(Double_greaterThanFromInteger, 2) { + const Double& right = Double::CheckedHandle(arguments->At(0)); + const Integer& left = Integer::CheckedHandle(arguments->At(1)); + const Bool& result = Bool::Handle(Bool::Get( + left.AsDoubleValue() > right.value())); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Double_equal, 2) { + const Double& left = Double::CheckedHandle(arguments->At(0)); + const Double& right = Double::CheckedHandle(arguments->At(1)); + bool result = right.IsNull() ? false : (left.value() == right.value()); + arguments->SetReturn(Bool::Handle(Bool::Get(result))); +} + + +DEFINE_NATIVE_ENTRY(Double_equalToInteger, 2) { + const Double& left = Double::CheckedHandle(arguments->At(0)); + const Integer& right = Integer::CheckedHandle(arguments->At(1)); + const Bool& result = + Bool::Handle(Bool::Get(left.value() == right.AsDoubleValue())); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Double_round, 1) { + const Double& arg = Double::CheckedHandle(arguments->At(0)); + arguments->SetReturn(Double::Handle(Double::New(round(arg.value())))); +} + +DEFINE_NATIVE_ENTRY(Double_floor, 1) { + const Double& arg = Double::CheckedHandle(arguments->At(0)); + arguments->SetReturn(Double::Handle(Double::New(floor(arg.value())))); +} + +DEFINE_NATIVE_ENTRY(Double_ceil, 1) { + const Double& arg = Double::CheckedHandle(arguments->At(0)); + arguments->SetReturn(Double::Handle(Double::New(ceil(arg.value())))); +} + + +DEFINE_NATIVE_ENTRY(Double_truncate, 1) { + const Double& arg = Double::CheckedHandle(arguments->At(0)); + arguments->SetReturn(Double::Handle(Double::New(trunc(arg.value())))); +} + + +DEFINE_NATIVE_ENTRY(Double_pow, 2) { + const double operand = Double::CheckedHandle(arguments->At(0)).value(); + const double exponent = Double::CheckedHandle(arguments->At(1)).value(); + arguments->SetReturn(Double::Handle(Double::New(pow(operand, exponent)))); +} + + +#if defined(TARGET_OS_MACOS) +// MAC OSX math library produces old style cast warning. +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif + +DEFINE_NATIVE_ENTRY(Double_toInt, 1) { + const Double& arg = Double::CheckedHandle(arguments->At(0)); + if (isinf(arg.value()) || isnan(arg.value())) { + GrowableArray args; + args.Add(&String::ZoneHandle(String::New( + "Infinity or NaN toInt"))); + Exceptions::ThrowByType(Exceptions::kBadNumberFormat, args); + } + double result = trunc(arg.value()); + if ((Smi::kMinValue <= result) && (result <= Smi::kMaxValue)) { + arguments->SetReturn(Smi::Handle(Smi::New(static_cast(result)))); + } else if ((Mint::kMinValue <= result) && (result <= Mint::kMaxValue)) { + arguments->SetReturn(Mint::Handle(Mint::New(static_cast(result)))); + } else { + arguments->SetReturn( + Bigint::Handle(BigintOperations::NewFromDouble(result))); + } +} + + +DEFINE_NATIVE_ENTRY(Double_isInfinite, 1) { + const Double& arg = Double::CheckedHandle(arguments->At(0)); + if (isinf(arg.value())) { + arguments->SetReturn(Bool::Handle(Bool::True())); + } else { + arguments->SetReturn(Bool::Handle(Bool::False())); + } +} + + +DEFINE_NATIVE_ENTRY(Double_isNaN, 1) { + const Double& arg = Double::CheckedHandle(arguments->At(0)); + if (isnan(arg.value())) { + arguments->SetReturn(Bool::Handle(Bool::True())); + } else { + arguments->SetReturn(Bool::Handle(Bool::False())); + } +} + + +DEFINE_NATIVE_ENTRY(Double_isNegative, 1) { + const Double& arg = Double::CheckedHandle(arguments->At(0)); + // Include negative zero, infinity. + if (signbit(arg.value()) && !isnan(arg.value())) { + arguments->SetReturn(Bool::Handle(Bool::True())); + } else { + arguments->SetReturn(Bool::Handle(Bool::False())); + } +} + +// Add here only functions using/referring to old-style casts. + +} // namespace dart + diff --git a/runtime/lib/double.dart b/runtime/lib/double.dart new file mode 100644 index 00000000000..0a4db25f966 --- /dev/null +++ b/runtime/lib/double.dart @@ -0,0 +1,200 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class Double implements double { + factory Double.fromInteger(int value) + native "Double_doubleFromInteger"; + int hashCode() { + try { + return toInt(); + } catch (BadNumberFormatException e) { + return 0; + } + } + double operator +(num other) { + return add_(other.toDouble()); + } + double add_(double other) native "Double_add"; + + double operator -(num other) { + return sub_(other.toDouble()); + } + double sub_(double other) native "Double_sub"; + + double operator *(num other) { + return mul_(other.toDouble()); + } + double mul_(double other) native "Double_mul"; + + double operator ~/(num other) { + return trunc_div_(other.toDouble()); + } + double trunc_div_(double other) native "Double_trunc_div"; + + double operator /(num other) { + return div_(other.toDouble()); + } + double div_(double other) native "Double_div"; + + double operator %(num other) { + return modulo_(other.toDouble()); + } + double modulo_(double other) native "Double_modulo"; + + double remainder(num other) { + return remainder_(other.toDouble()); + } + double remainder_(double other) native "Double_remainder"; + + double operator negate() { + return 0.0 - this; + } + bool operator ==(other) { + if (!(other is num)) return false; + return equal_(other.toDouble()); + } + bool equal_(double other)native "Double_equal"; + bool equalToInteger(int other) native "Double_equalToInteger"; + bool operator <(num other) { + return other > this; + } + bool operator >(num other) { + return greaterThan_(other.toDouble()); + } + bool greaterThan_(double other) native "Double_greaterThan"; + bool operator >=(num other) { + return (this == other) || (this > other); + } + bool operator <=(num other) { + return (this == other) || (this < other); + } + double addFromInteger(int other) { + return new Double.fromInteger(other) + this; + } + double subFromInteger(int other) { + return new Double.fromInteger(other) - this; + } + double mulFromInteger(int other) { + return new Double.fromInteger(other) * this; + } + double truncDivFromInteger(int other) { + return new Double.fromInteger(other) ~/ this; + } + double moduloFromInteger(int other) { + return new Double.fromInteger(other) % this; + } + double remainderFromInteger(int other) { + return new Double.fromInteger(other).remainder(this); + } + + bool greaterThanFromInteger(int other) native "Double_greaterThanFromInteger"; + + bool isEven() { + // TODO(floitsch): find more efficient way to implement Double.isEven. + return this % 2.0 == 0.0; + } + bool isOdd() { + // TODO(floitsch): find more efficient way to implement Double.isOdd. + return this % 2.0 == 1.0; + } + bool isNegative() native "Double_isNegative"; + bool isInfinite() native "Double_isInfinite"; + bool isNaN() native "Double_isNaN"; + + double abs() { + return this < 0.0 ? -this : this; + } + + double round() native "Double_round"; + double floor() native "Double_floor"; + double ceil () native "Double_ceil"; + double truncate() native "Double_truncate"; + int toInt() native "Double_toInt"; + double toDouble() { return this; } + + double pow(num exponent) { + return pow_(exponent.toDouble()); + } + double pow_(double exponent) native "Double_pow"; + + String toStringAsFixed(int fractionDigits) { + // See ECMAScript-262, 15.7.4.5 for details. + + // Step 2. + if (fractionDigits < 0 || fractionDigits > 20) { + // TODO(antonm): should be proper RangeError or Dart counterpart. + throw "Range error"; + } + + // Step 3. + double x = this; + + // Step 4. + if (x.isNaN()) { + return "NaN"; + } + + // Step 5. + String s = ""; + + // Step 6. + if (x.isNegative()) { + s = "-"; + x = -x; + } + + // Step 7. + String m; + if (x > 10e21) { + m = x.toString(); + } else { + // Step 8. + + // Step 8.a. + // This is an approximation for n from the standard. + int n = (x * (10.0).pow(fractionDigits)).round().toInt(); + + // Step 8.b. + m = n.toString(); + + // Step 8.c. + if (fractionDigits != 0) { + // Step 8.c.i. + int k = m.length; + // Step 8.c.ii. + if (k <= fractionDigits) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < fractionDigits + 1 - k; i++) { + sb.add("0"); + } + String z = sb.toString(); + m = z + m; + k = fractionDigits + 1; + } + // Step 8.c.iii. + String a = m.substring(0, k - fractionDigits); + String b = m.substringToEnd(k - fractionDigits); + // Step 8.c.iv. + m = a + "." + b; + } + } + + // Step 9. + return s + m; + } + String toStringAsExponential(int fractionDigits) { + throw "Double.toStringAsExponential unimplemented."; + } + String toStringAsPrecision(int precision) { + throw "Double.toStringAsPrecision unimplemented."; + } + String toRadixString(int radix) { + throw "Double.toRadixString unimplemented."; + } + int compareTo(Comparable other) { + if (this == other) return 0; + if (this < other) return -1; + return 1; + } +} diff --git a/runtime/lib/error.cc b/runtime/lib/error.cc new file mode 100644 index 00000000000..37ddc92f57e --- /dev/null +++ b/runtime/lib/error.cc @@ -0,0 +1,288 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "lib/error.h" + +#include "vm/bootstrap_natives.h" +#include "vm/exceptions.h" +#include "vm/object_store.h" +#include "vm/runtime_entry.h" +#include "vm/stack_frame.h" + +namespace dart { + +DECLARE_FLAG(bool, print_stack_trace_at_throw); + + +// Static helpers for allocating, initializing, and throwing an error instance. + +// Return the script of the Dart function that called the native entry or the +// runtime entry. The frame iterator points to the callee. +static RawScript* GetCallerScript(DartFrameIterator* iterator) { + DartFrame* caller_frame = iterator->NextFrame(); + ASSERT(caller_frame != NULL); + const Function& caller = Function::Handle(caller_frame->LookupDartFunction()); + ASSERT(!caller.IsNull()); + const Class& caller_class = Class::Handle(caller.owner()); + return caller_class.script(); +} + + +// Allocate a new instance of the given class name. +// TODO(hausner): Rename this NewCoreInstance to call out the fact that +// the class name is resolved in the core library implicitly? +static RawInstance* NewInstance(const char* class_name) { + const String& cls_name = String::Handle(String::NewSymbol(class_name)); + const Library& core_lib = Library::Handle(Library::CoreLibrary()); + Class& cls = Class::Handle(core_lib.LookupClass(cls_name)); + ASSERT(!cls.IsNull()); + // There are no parameterized error types, so no need to set type arguments. + return Instance::New(cls); +} + + +// Assign the value to the field given by its name in the given instance. +static void SetField(const Instance& instance, + const Class& cls, + const char* field_name, + const Object& value) { + const Field& field = Field::Handle(cls.LookupInstanceField( + String::Handle(String::NewSymbol(field_name)))); + ASSERT(!field.IsNull()); + instance.SetField(field, value); +} + + +// Initialize the fields 'url', 'line', and 'column' in the given instance +// according to the given token location in the given script. +static void SetLocationFields(const Instance& instance, + const Class& cls, + const Script& script, + intptr_t location) { + SetField(instance, cls, "url", String::Handle(script.url())); + intptr_t line, column; + script.GetTokenLocation(location, &line, &column); + SetField(instance, cls, "line", Smi::Handle(Smi::New(line))); + SetField(instance, cls, "column", Smi::Handle(Smi::New(column))); +} + + +// Allocate and throw a new AssertError. +// Arg0: index of the first token of the failed assertion. +// Arg1: index of the first token after the failed assertion. +// Return value: none, throws an exception. +DEFINE_NATIVE_ENTRY(AssertError_throwNew, 2) { + intptr_t assertion_start = Smi::CheckedHandle(arguments->At(0)).Value(); + intptr_t assertion_end = Smi::CheckedHandle(arguments->At(1)).Value(); + + // Allocate a new instance of type AssertError. + const Instance& assert_error = Instance::Handle(NewInstance("AssertError")); + + // Initialize 'url', 'line', and 'column' fields. + DartFrameIterator iterator; + iterator.NextFrame(); // Skip native call. + const Script& script = Script::Handle(GetCallerScript(&iterator)); + const Class& cls = Class::Handle(assert_error.clazz()); + SetLocationFields(assert_error, cls, script, assertion_start); + + // Initialize field 'failed_assertion' with source snippet. + intptr_t from_line, from_column; + script.GetTokenLocation(assertion_start, &from_line, &from_column); + intptr_t to_line, to_column; + script.GetTokenLocation(assertion_end, &to_line, &to_column); + SetField(assert_error, cls, "failedAssertion", String::Handle( + script.GetSnippet(from_line, from_column, to_line, to_column))); + + // Throw AssertError instance. + Exceptions::Throw(assert_error); + UNREACHABLE(); +} + + +// Allocate, initialize, and throw a TypeError. +static void ThrowTypeError(intptr_t location, + const String& src_type_name, + const String& dst_type_name, + const String& dst_name) { + // Allocate a new instance of TypeError. + const Instance& type_error = Instance::Handle(NewInstance("TypeError")); + + // Initialize 'url', 'line', and 'column' fields. + DartFrameIterator iterator; + const Script& script = Script::Handle(GetCallerScript(&iterator)); + const Class& cls = Class::Handle(type_error.clazz()); + // Location fields are defined in AssertError, the superclass of TypeError. + const Class& assert_error_class = Class::Handle(cls.SuperClass()); + SetLocationFields(type_error, assert_error_class, script, location); + + // Initialize field 'failedAssertion' in AssertError superclass. + // Printing the src_obj value would be possible, but ToString() is expensive + // and not meaningful for all classes, so we just print '$expr instanceof...'. + // Users should look at TypeError.ToString(), which contains more useful + // information than AssertError.failedAssertion. + String& failed_assertion = String::Handle(String::New("$expr instanceof ")); + failed_assertion = String::Concat(failed_assertion, dst_type_name); + SetField(type_error, assert_error_class, "failedAssertion", failed_assertion); + + // Initialize field 'srcType'. + SetField(type_error, cls, "srcType", src_type_name); + + // Initialize field 'dstType'. + SetField(type_error, cls, "dstType", dst_type_name); + + // Initialize field 'dstName'. + SetField(type_error, cls, "dstName", dst_name); + + // Type errors in the core library may be difficult to diagnose. + // Print type error information before throwing the error when debugging. + if (FLAG_print_stack_trace_at_throw) { + OS::Print("Type %s is not assignable to type %s of %s.\n", + src_type_name.ToCString(), + dst_type_name.ToCString(), + dst_name.ToCString()); + } + // Throw TypeError instance. + Exceptions::Throw(type_error); + UNREACHABLE(); +} + + +// Allocate and throw a new FallThroughError. +// Arg0: index of the case clause token into which we fall through. +// Return value: none, throws an exception. +DEFINE_NATIVE_ENTRY(FallThroughError_throwNew, 1) { + intptr_t fallthrough_pos = Smi::CheckedHandle(arguments->At(0)).Value(); + + // Allocate a new instance of type FallThroughError. + const Instance& fallthrough_error = + Instance::Handle(NewInstance("FallThroughError")); + ASSERT(!fallthrough_error.IsNull()); + + // Initialize 'url' and 'line' fields. + DartFrameIterator iterator; + iterator.NextFrame(); // Skip native call. + const Script& script = Script::Handle(GetCallerScript(&iterator)); + const Class& cls = Class::Handle(fallthrough_error.clazz()); + SetField(fallthrough_error, cls, "url", String::Handle(script.url())); + intptr_t line, column; + script.GetTokenLocation(fallthrough_pos, &line, &column); + SetField(fallthrough_error, cls, "line", Smi::Handle(Smi::New(line))); + + // Throw FallThroughError instance. + Exceptions::Throw(fallthrough_error); + UNREACHABLE(); +} + + +// Check that the type of the given instance is assignable to the given type. +// Arg0: index of the token of the assignment (source location). +// Arg1: instance being assigned. +// Arg2: type being assigned to. +// Arg3: type arguments of the instantiator of the type being assigned to. +// Arg4: name of instance being assigned to. +// Return value: instance if assignable, otherwise throw a TypeError. +DEFINE_RUNTIME_ENTRY(TypeCheck, 5) { + ASSERT(arguments.Count() == kTypeCheckRuntimeEntry.argument_count()); + intptr_t location = Smi::CheckedHandle(arguments.At(0)).Value(); + const Instance& src_instance = Instance::CheckedHandle(arguments.At(1)); + const Type& dst_type = Type::CheckedHandle(arguments.At(2)); + const TypeArguments& dst_type_instantiator = + TypeArguments::CheckedHandle(arguments.At(3)); + const String& dst_name = String::CheckedHandle(arguments.At(4)); + ASSERT(!dst_type.IsVarType()); // No need to check assignment to 'var type'. + ASSERT(!src_instance.IsNull()); // Already checked in inlined code. + + if (!src_instance.IsAssignableTo(dst_type, dst_type_instantiator)) { + const Type& src_type = Type::Handle(src_instance.GetType()); + const String& src_type_name = String::Handle(src_type.Name()); + String& dst_type_name = String::Handle(); + if (!dst_type.IsInstantiated()) { + // Instantiate dst_type before reporting the error. + const Type& instantiated_dst_type = Type::Handle( + dst_type.InstantiateFrom(dst_type_instantiator, 0)); + dst_type_name = instantiated_dst_type.Name(); + } else { + dst_type_name = dst_type.Name(); + } + ThrowTypeError(location, src_type_name, dst_type_name, dst_name); + UNREACHABLE(); + } + arguments.SetReturn(src_instance); +} + + +// Check that the type of the given object is allowed in conditional context. +// Arg0: index of the token of the assignment (source location). +// Arg1: object being checked. +// Return value: checked value, otherwise allocate and throw a TypeError. +DEFINE_RUNTIME_ENTRY(ConditionTypeCheck, 2) { + ASSERT(arguments.Count() == + kConditionTypeCheckRuntimeEntry.argument_count()); + intptr_t location = Smi::CheckedHandle(arguments.At(0)).Value(); + const Instance& src_instance = Instance::CheckedHandle(arguments.At(1)); + + const char* msg = "boolean expression"; + if (src_instance.IsNull() || !src_instance.IsBool()) { + const Type& bool_interface = + Type::ZoneHandle(Isolate::Current()->object_store()->bool_interface()); + const Type& src_type = Type::Handle(src_instance.GetType()); + const String& src_type_name = String::Handle(src_type.Name()); + const String& bool_type_name = String::Handle(bool_interface.Name()); + ThrowTypeError(location, src_type_name, bool_type_name, + String::Handle(String::NewSymbol(msg))); + UNREACHABLE(); + } + + arguments.SetReturn(src_instance); +} + + +// Check that the type of each element of the given array is assignable to the +// given type. +// Arg0: index of the token of the rest argument declaration (source location). +// Arg1: rest argument array. +// Arg2: element declaration type. +// Arg3: type arguments of the instantiator of the element declaration type. +// Arg4: name of object being assigned to, i.e. name of rest argument. +// Return value: null if assignable, otherwise allocate and throw a TypeError. +DEFINE_RUNTIME_ENTRY(RestArgumentTypeCheck, 5) { + ASSERT(arguments.Count() == + kRestArgumentTypeCheckRuntimeEntry.argument_count()); + intptr_t location = Smi::CheckedHandle(arguments.At(0)).Value(); + const Array& rest_array = Array::CheckedHandle(arguments.At(1)); + const Type& element_type = Type::CheckedHandle(arguments.At(2)); + const TypeArguments& element_type_instantiator = + TypeArguments::CheckedHandle(arguments.At(3)); + const String& rest_name = String::CheckedHandle(arguments.At(4)); + ASSERT(!element_type.IsVarType()); // No need to check assignment. + ASSERT(!rest_array.IsNull()); + + Instance& elem = Instance::Handle(); + for (intptr_t i = 0; i < rest_array.Length(); i++) { + elem ^= rest_array.At(i); + if (!elem.IsNull() && + !elem.IsAssignableTo(element_type, element_type_instantiator)) { + // Allocate and throw a new instance of TypeError. + char buf[256]; + OS::SNPrint(buf, sizeof(buf), "%s[%d]", + rest_name.ToCString(), static_cast(i)); + const String& src_type_name = + String::Handle(Type::Handle(elem.GetType()).Name()); + String& dst_type_name = String::Handle(); + if (!element_type.IsInstantiated()) { + // Instantiate element_type before reporting the error. + const Type& instantiated_element_type = Type::Handle( + element_type.InstantiateFrom(element_type_instantiator, 0)); + dst_type_name = instantiated_element_type.Name(); + } else { + dst_type_name = element_type.Name(); + } + const String& dst_name = String::Handle(String::New(buf)); + ThrowTypeError(location, src_type_name, dst_type_name, dst_name); + UNREACHABLE(); + } + } +} + +} // namespace dart diff --git a/runtime/lib/error.dart b/runtime/lib/error.dart new file mode 100644 index 00000000000..4ad2b70a242 --- /dev/null +++ b/runtime/lib/error.dart @@ -0,0 +1,55 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Errors are created and thrown by DartVM only. + +class AssertError { + factory AssertError._uninstantiable() { + throw const UnsupportedOperationException( + "AssertError can only be allocated by the VM"); + } + static throwNew(int assertionStart, int assertionEnd) + native "AssertError_throwNew"; + String toString() { + return "Failed assertion: '$failedAssertion' is not true " + + "in $url at line $line, column $column."; + } + final String failedAssertion; + final String url; + final int line; + final int column; +} + +class TypeError extends AssertError { + factory TypeError._uninstantiable() { + throw const UnsupportedOperationException( + "TypeError can only be allocated by the VM"); + } + String toString() { + return "Failed type check: type $srcType is not assignable to type " + + "$dstType of $dstName in $url at line " + + "$line, column $column."; + } + final String srcType; + final String dstType; + final String dstName; +} + +class FallThroughError { + factory FallThroughError._uninstantiable() { + throw const UnsupportedOperationException( + "FallThroughError can only be allocated by the VM"); + } + static throwNew(int case_clause_pos) native "FallThroughError_throwNew"; + String toString() { + return "Switch case fall-through in $url at line $line."; + } + final String url; + final int line; +} + +class InternalError { + const InternalError(this._msg); + String toString() => "InternalError: '${_msg}'"; + final String _msg; +} diff --git a/runtime/lib/error.h b/runtime/lib/error.h new file mode 100644 index 00000000000..70ccbb419ff --- /dev/null +++ b/runtime/lib/error.h @@ -0,0 +1,19 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef LIB_ERROR_H_ +#define LIB_ERROR_H_ + +#include "vm/runtime_entry.h" + +namespace dart { + +DECLARE_RUNTIME_ENTRY(ConditionTypeCheck); +DECLARE_RUNTIME_ENTRY(RestArgumentTypeCheck); +DECLARE_RUNTIME_ENTRY(TypeCheck); + +} // namespace dart + +#endif // LIB_ERROR_H_ + diff --git a/runtime/lib/growable_array.dart b/runtime/lib/growable_array.dart new file mode 100644 index 00000000000..9e9fe2e1384 --- /dev/null +++ b/runtime/lib/growable_array.dart @@ -0,0 +1,182 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class GrowableObjectArray implements Array { + Array backingArray; + + void copyFrom(Array src, int srcStart, int dstStart, int count) { + Arrays.copy(src, srcStart, this, dstStart, count); + } + + // The length of this growable array. It is always less than the + // length of the backing array. + int _length; + // Constant used by indexOf and lastIndexOf when the element given + // is not in the array. + static final int ABSENT = -1; + + GrowableObjectArray() + : _length = 0, backingArray = new ObjectArray(4) {} + + GrowableObjectArray.withCapacity(int capacity) { + _length = 0; + if (capacity <= 0) { + capacity = 4; + } + backingArray = new ObjectArray(capacity); + } + + GrowableObjectArray._usingArray(Array array) { + backingArray = array; + _length = array.length; + if (_length == 0) { + grow(4); + } + } + + factory GrowableObjectArray.from(Collection other) { + Array result = new GrowableObjectArray(); + result.addAll(other); + return result; + } + + int get length() { + return _length; + } + + void set length(int new_length) { + if (new_length >= backingArray.length) { + grow(new_length); + } else { + for (int i = new_length; i < _length; i++) { + backingArray[i] = null; + } + } + _length = new_length; + } + + T operator [](int index) { + if (index >= _length) { + throw new IndexOutOfRangeException(index); + } + return backingArray[index]; + } + + void operator []=(int index, T value) { + if (index >= _length) { + throw new IndexOutOfRangeException(index); + } + backingArray[index] = value; + } + + void grow(int capacity) { + Array newArray = new ObjectArray(capacity); + int length = backingArray.length; + for (int i = 0; i < length; i++) { + newArray[i] = backingArray[i]; + } + backingArray = newArray; + } + + int add(T value) { + if (_length == backingArray.length) { + grow(_length * 2); + } + backingArray[_length] = value; + return ++_length; + } + + void addLast(T element) { + add(element); + } + + void addAll(Collection collection) { + for (T elem in collection) { + add(elem); + } + } + + T removeLast() { + _length--; + return backingArray[_length]; + } + + T last() { + if (_length === 0) { + throw new IndexOutOfRangeException(-1); + } + return backingArray[_length - 1]; + } + + int indexOf(T element, int startIndex) { + return Arrays.indexOf(backingArray, element, startIndex, _length); + } + + int lastIndexOf(T element, int startIndex) { + return Arrays.lastIndexOf(backingArray, element, startIndex); + } + + /** + * Collection interface. + */ + + void forEach(f(T element)) { + // TODO(srdjan): Use Collections.forEach(this, f); + // Using backingArray directly improves DeltaBlue performance by 25%. + for (int i = 0; i < _length; i++) { + f(backingArray[i]); + } + } + + Collection filter(bool f(T element)) { + return Collections.filter(this, new GrowableObjectArray(), f); + } + + bool every(bool f(T element)) { + return Collections.every(this, f); + } + + bool some(bool f(T element)) { + return Collections.some(this, f); + } + + bool isEmpty() { + return this.length === 0; + } + + void clear() { + this.length = 0; + } + + void sort(int compare(T a, T b)) { + DualPivotQuicksort.sort(this, compare); + } + + Iterator iterator() { + return new VariableSizeArrayIterator(this); + } +} + + +// Iterator for arrays with variable size. +class VariableSizeArrayIterator implements Iterator { + VariableSizeArrayIterator(GrowableObjectArray array) + : _array = array, _pos = 0 { + } + + bool hasNext() { + return _array._length > _pos; + } + + T next() { + if (!hasNext()) { + throw const NoMoreElementsException(); + } + return _array[_pos++]; + } + + final GrowableObjectArray _array; + int _pos; +} + diff --git a/runtime/lib/immutable_map.dart b/runtime/lib/immutable_map.dart new file mode 100644 index 00000000000..3f65dbe723d --- /dev/null +++ b/runtime/lib/immutable_map.dart @@ -0,0 +1,108 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Immutable map class for compiler generated map literals. + +class ImmutableMap implements Map { + final ImmutableArray kvPairs_; + + const ImmutableMap(ImmutableArray keyValuePairs) + : kvPairs_ = keyValuePairs; + + + V operator [](K key) { + // TODO(hausner): Since the keys are sorted, we could do a binary + // search. But is it worth it? + for (int i = 0; i < kvPairs_.length - 1; i += 2) { + if (key == kvPairs_[i]) { + return kvPairs_[i+1]; + } + } + return null; + } + + bool isEmpty() { + return kvPairs_.length === 0; + } + + int get length() { + return kvPairs_.length ~/ 2; + } + + void forEach(void f(K key, V value)) { + for (int i = 0; i < kvPairs_.length; i += 2) { + f(kvPairs_[i], kvPairs_[i+1]); + } + } + + Collection getKeys() { + int numKeys = length; + Array array = new Array(numKeys); + for (int i = 0; i < numKeys; i++) { + array[i] = kvPairs_[i*2]; + } + return array; + } + + Collection getValues() { + int numValues = length; + Array array = new Array(numValues); + for (int i = 0; i < numValues; i++) { + array[i] = kvPairs_[i*2 + 1]; + } + return array; + } + + bool containsKey(K key) { + for (int i = 0; i < kvPairs_.length; i += 2) { + if (key == kvPairs_[i]) { + return true; + } + } + return false; + } + + bool containsValue(V value) { + for (int i = 1; i < kvPairs_.length; i += 2) { + if (value == kvPairs_[i]) { + return true; + } + } + return false; + } + + void operator []=(K key, V value) { + throw const IllegalAccessException(); + } + + V putIfAbsent(K key, V ifAbsent()) { + throw const IllegalAccessException(); + } + + void clear() { + throw const IllegalAccessException(); + } + + V remove(K key) { + throw const IllegalAccessException(); + } + + String toString() { + // TODO(srdjan): Extend text representation. + return "ImmutableMap"; + } +} + + +class MutableMap { + // [elements] contains n key-value pairs. The keys are at position + // 2*n, the values at position 2*n+1. + static fromLiteral(Array elements) { + var map = new LinkedHashMap(); + var len = elements.length; + for (int i = 1; i < len; i += 2) { + map[elements[i-1]] = elements[i]; + } + return map; + } +} diff --git a/runtime/lib/integers.cc b/runtime/lib/integers.cc new file mode 100644 index 00000000000..de5b6b5bab8 --- /dev/null +++ b/runtime/lib/integers.cc @@ -0,0 +1,586 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bootstrap_natives.h" + +#include "vm/bigint_operations.h" +#include "vm/dart_entry.h" +#include "vm/native_entry.h" +#include "vm/object.h" + +namespace dart { + +DEFINE_FLAG(bool, trace_intrinsified_natives, false, + "Report if any of the intrinsified natives are called"); + +// Smi natives. + +// Return the most compact presentation of an integer. +static RawInteger* AsInteger(const Integer& value) { + if (value.IsSmi()) return value.raw(); + if (value.IsMint()) { + Mint& mint = Mint::Handle(); + mint ^= value.raw(); + if (Smi::IsValid64(mint.value())) { + return Smi::New(mint.value()); + } else { + return value.raw(); + } + } + ASSERT(value.IsBigint()); + Bigint& big_value = Bigint::Handle(); + big_value ^= value.raw(); + if (BigintOperations::FitsIntoSmi(big_value)) { + return BigintOperations::ToSmi(big_value); + } else if (BigintOperations::FitsIntoInt64(big_value)) { + return Mint::New(BigintOperations::ToInt64(big_value)); + } else { + return big_value.raw(); + } +} + + +// Returns value in form of a RawBigint. +static RawBigint* AsBigint(const Integer& value) { + ASSERT(!value.IsNull()); + if (value.IsSmi()) { + Smi& smi = Smi::Handle(); + smi ^= value.raw(); + return BigintOperations::NewFromSmi(smi); + } else if (value.IsMint()) { + Mint& mint = Mint::Handle(); + mint ^= value.raw(); + return BigintOperations::NewFromInt64(mint.value()); + } else { + ASSERT(value.IsBigint()); + Bigint& big = Bigint::Handle(); + big ^= value.raw(); + ASSERT(!BigintOperations::FitsIntoSmi(big)); + return big.raw(); + } +} + + +static bool Are64bitOperands(const Integer& op1, const Integer& op2) { + return !op1.IsBigint() && !op2.IsBigint(); +} + + +static RawInteger* IntegerBitOperation(Token::Kind kind, + const Integer& op1_int, + const Integer& op2_int) { + if (op1_int.IsSmi() && op2_int.IsSmi()) { + Smi& op1 = Smi::Handle(); + Smi& op2 = Smi::Handle(); + op1 ^= op1_int.raw(); + op2 ^= op2_int.raw(); + intptr_t result = 0; + switch (kind) { + case Token::kBIT_AND: + result = op1.Value() & op2.Value(); + break; + case Token::kBIT_OR: + result = op1.Value() | op2.Value(); + break; + case Token::kBIT_XOR: + result = op1.Value() ^ op2.Value(); + break; + default: + UNIMPLEMENTED(); + } + ASSERT(Smi::IsValid(result)); + return Smi::New(result); + } else if (Are64bitOperands(op1_int, op2_int)) { + int64_t a = op1_int.AsInt64Value(); + int64_t b = op2_int.AsInt64Value(); + switch (kind) { + case Token::kBIT_AND: + return Integer::New(a & b); + case Token::kBIT_OR: + return Integer::New(a | b); + case Token::kBIT_XOR: + return Integer::New(a ^ b); + default: + UNIMPLEMENTED(); + } + } else if (op1_int.IsSmi()) { + return IntegerBitOperation(kind, op2_int, op1_int); + } else if (op2_int.IsSmi()) { + Bigint& op1 = Bigint::Handle(AsBigint(op1_int)); + Smi& op2 = Smi::Handle(); + op2 ^= op2_int.raw(); + switch (kind) { + case Token::kBIT_AND: + return BigintOperations::BitAndWithSmi(op1, op2); + case Token::kBIT_OR: + return BigintOperations::BitOrWithSmi(op1, op2); + case Token::kBIT_XOR: + return BigintOperations::BitXorWithSmi(op1, op2); + default: + UNIMPLEMENTED(); + } + } else { + Bigint& op1 = Bigint::Handle(AsBigint(op1_int)); + Bigint& op2 = Bigint::Handle(AsBigint(op2_int)); + switch (kind) { + case Token::kBIT_AND: + return BigintOperations::BitAnd(op1, op2); + case Token::kBIT_OR: + return BigintOperations::BitOr(op1, op2); + case Token::kBIT_XOR: + return BigintOperations::BitXor(op1, op2); + default: + UNIMPLEMENTED(); + } + } + return Integer::null(); +} + + +// Returns false if integer is in wrong representation, e.g., as is a Bigint +// when it could have been a Smi. +static bool CheckInteger(const Integer& i) { + if (i.IsBigint()) { + Bigint& bigint = Bigint::Handle(); + bigint ^= i.raw(); + return !BigintOperations::FitsIntoSmi(bigint) && + !BigintOperations::FitsIntoInt64(bigint); + } + if (i.IsMint()) { + Mint& mint = Mint::Handle(); + mint ^= i.raw(); + return !Smi::IsValid64(mint.value()); + } + return true; +} + + +DEFINE_NATIVE_ENTRY(Integer_bitAndFromInteger, 2) { + const Integer& right = Integer::CheckedHandle(arguments->At(0)); + const Integer& left = Integer::CheckedHandle(arguments->At(1)); + ASSERT(CheckInteger(right)); + ASSERT(CheckInteger(left)); + if (FLAG_trace_intrinsified_natives) { + OS::Print("Integer_bitAndFromInteger %s & %s\n", + right.ToCString(), left.ToCString()); + } + Integer& result = Integer::Handle( + IntegerBitOperation(Token::kBIT_AND, left, right)); + arguments->SetReturn(Integer::Handle(AsInteger(result))); +} + + +DEFINE_NATIVE_ENTRY(Integer_bitOrFromInteger, 2) { + const Integer& right = Integer::CheckedHandle(arguments->At(0)); + const Integer& left = Integer::CheckedHandle(arguments->At(1)); + ASSERT(CheckInteger(right)); + ASSERT(CheckInteger(left)); + if (FLAG_trace_intrinsified_natives) { + OS::Print("Integer_bitOrFromInteger %s | %s\n", + left.ToCString(), right.ToCString()); + } + Integer& result = Integer::Handle( + IntegerBitOperation(Token::kBIT_OR, left, right)); + arguments->SetReturn(Integer::Handle(AsInteger(result))); +} + + +DEFINE_NATIVE_ENTRY(Integer_bitXorFromInteger, 2) { + const Integer& right = Integer::CheckedHandle(arguments->At(0)); + const Integer& left = Integer::CheckedHandle(arguments->At(1)); + ASSERT(CheckInteger(right)); + ASSERT(CheckInteger(left)); + if (FLAG_trace_intrinsified_natives) { + OS::Print("Integer_bitXorFromInteger %s ^ %s\n", + left.ToCString(), right.ToCString()); + } + Integer& result = Integer::Handle( + IntegerBitOperation(Token::kBIT_XOR, left, right)); + arguments->SetReturn(Integer::Handle(AsInteger(result))); +} + + +// The result is invalid if it is outside the range +// Smi::kMaxValue..Smi::kMinValue. +static int64_t BinaryOpWithTwoSmis(Token::Kind operation, + const Smi& left, + const Smi& right) { + switch (operation) { + case Token::kADD: + return left.Value() + right.Value(); + case Token::kSUB: + return left.Value() - right.Value(); + case Token::kMUL: { +#if defined(TARGET_ARCH_X64) + // Overflow check for 64-bit platforms unimplemented. + UNIMPLEMENTED(); + return 0; +#else + int64_t result_64 = + static_cast(left.Value()) * + static_cast(right.Value()); + return result_64; +#endif + } + case Token::kTRUNCDIV: + return left.Value() / right.Value(); + case Token::kMOD: { + intptr_t remainder = left.Value() % right.Value(); + if (remainder < 0) { + if (right.Value() < 0) { + return remainder - right.Value(); + } else { + return remainder + right.Value(); + } + } else { + return remainder; + } + } + default: + UNIMPLEMENTED(); + return 0; + } +} + + +static RawBigint* BinaryOpWithTwoBigints(Token::Kind operation, + const Bigint& left, + const Bigint& right) { + switch (operation) { + case Token::kADD: + return BigintOperations::Add(left, right); + case Token::kSUB: + return BigintOperations::Subtract(left, right); + case Token::kMUL: + return BigintOperations::Multiply(left, right); + case Token::kTRUNCDIV: + return BigintOperations::Divide(left, right); + case Token::kMOD: + return BigintOperations::Modulo(left, right); + default: + UNIMPLEMENTED(); + return Bigint::null(); + } +} + + +// TODO(srdjan): Implement correct overflow checking before allowing 64 bit +// operands. +static bool AreBoth64bitOperands(const Integer& op1, const Integer& op2) { + return false; +} + + +static RawInteger* IntegerBinopHelper(Token::Kind operation, + const Integer& left_int, + const Integer& right_int) { + if (left_int.IsSmi() && right_int.IsSmi()) { + Smi& left_smi = Smi::Handle(); + Smi& right_smi = Smi::Handle(); + left_smi ^= left_int.raw(); + right_smi ^= right_int.raw(); + int64_t result = BinaryOpWithTwoSmis(operation, left_smi, right_smi); + if (Smi::IsValid64(result)) { + return Smi::New(result); + } else { + // Overflow to Mint. + return Mint::New(result); + } + } else if (AreBoth64bitOperands(left_int, right_int)) { + // TODO(srdjan): Test for overflow of result instead of operand + // types. + const int64_t a = left_int.AsInt64Value(); + const int64_t b = right_int.AsInt64Value(); + switch (operation) { + case Token::kADD: + return Integer::New(a + b); + case Token::kSUB: + return Integer::New(a - b); + case Token::kMUL: + return Integer::New(a * b); + case Token::kTRUNCDIV: + return Integer::New(a / b); + case Token::kMOD: { + int64_t remainder = a % b; + int64_t c = 0; + if (remainder < 0) { + if (b < 0) { + c = remainder - b; + } else { + c = remainder + b; + } + } else { + c = remainder; + } + return Integer::New(c); + } + default: + UNIMPLEMENTED(); + } + } + const Bigint& left_big = Bigint::Handle(AsBigint(left_int)); + const Bigint& right_big = Bigint::Handle(AsBigint(right_int)); + const Bigint& result = + Bigint::Handle(BinaryOpWithTwoBigints(operation, left_big, right_big)); + return Integer::Handle(AsInteger(result)).raw(); +} + + +DEFINE_NATIVE_ENTRY(Integer_addFromInteger, 2) { + const Integer& right_int = Integer::CheckedHandle(arguments->At(0)); + const Integer& left_int = Integer::CheckedHandle(arguments->At(1)); + ASSERT(CheckInteger(right_int)); + ASSERT(CheckInteger(left_int)); + if (FLAG_trace_intrinsified_natives) { + OS::Print("Integer_addFromInteger %s + %s\n", + left_int.ToCString(), right_int.ToCString()); + } + const Integer& result = + Integer::Handle(IntegerBinopHelper(Token::kADD, left_int, right_int)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Integer_subFromInteger, 2) { + const Integer& right_int = Integer::CheckedHandle(arguments->At(0)); + const Integer& left_int = Integer::CheckedHandle(arguments->At(1)); + ASSERT(CheckInteger(right_int)); + ASSERT(CheckInteger(left_int)); + if (FLAG_trace_intrinsified_natives) { + OS::Print("Integer_subFromInteger %s - %s\n", + left_int.ToCString(), right_int.ToCString()); + } + const Integer& result = + Integer::Handle(IntegerBinopHelper(Token::kSUB, left_int, right_int)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Integer_mulFromInteger, 2) { + const Integer& right_int = Integer::CheckedHandle(arguments->At(0)); + const Integer& left_int = Integer::CheckedHandle(arguments->At(1)); + ASSERT(CheckInteger(right_int)); + ASSERT(CheckInteger(left_int)); + if (FLAG_trace_intrinsified_natives) { + OS::Print("Integer_mulFromInteger %s * %s\n", + left_int.ToCString(), right_int.ToCString()); + } + const Integer& result = + Integer::Handle(IntegerBinopHelper(Token::kMUL, left_int, right_int)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Integer_truncDivFromInteger, 2) { + const Integer& right_int = Integer::CheckedHandle(arguments->At(0)); + const Integer& left_int = Integer::CheckedHandle(arguments->At(1)); + ASSERT(CheckInteger(right_int)); + ASSERT(CheckInteger(left_int)); + ASSERT(!right_int.IsZero()); + const Integer& result = Integer::Handle( + IntegerBinopHelper(Token::kTRUNCDIV, left_int, right_int)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Integer_moduloFromInteger, 2) { + const Integer& right_int = Integer::CheckedHandle(arguments->At(0)); + const Integer& left_int = Integer::CheckedHandle(arguments->At(1)); + ASSERT(CheckInteger(right_int)); + ASSERT(CheckInteger(right_int)); + if (FLAG_trace_intrinsified_natives) { + OS::Print("Integer_moduloFromInteger %s mod %s\n", + left_int.ToCString(), right_int.ToCString()); + } + if (right_int.IsZero()) { + // Should have been caught before calling into runtime. + UNIMPLEMENTED(); + } + const Integer& result = + Integer::Handle(IntegerBinopHelper(Token::kMOD, left_int, right_int)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Integer_greaterThanFromInteger, 2) { + const Integer& right = Integer::CheckedHandle(arguments->At(0)); + const Integer& left = Integer::CheckedHandle(arguments->At(1)); + ASSERT(CheckInteger(right)); + ASSERT(CheckInteger(left)); + if (FLAG_trace_intrinsified_natives) { + OS::Print("Integer_greaterThanFromInteger %s > %s\n", + left.ToCString(), right.ToCString()); + } + const Bool& result = Bool::Handle(Bool::Get(left.CompareWith(right) == 1)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Integer_equalToInteger, 2) { + const Integer& left = Integer::CheckedHandle(arguments->At(0)); + const Integer& right = Integer::CheckedHandle(arguments->At(1)); + ASSERT(CheckInteger(left)); + ASSERT(CheckInteger(right)); + if (FLAG_trace_intrinsified_natives) { + OS::Print("Integer_equalToInteger %s == %s\n", + left.ToCString(), right.ToCString()); + } + const Bool& result = Bool::Handle(Bool::Get(left.CompareWith(right) == 0)); + arguments->SetReturn(result); +} + + +static int HighestBit(int64_t v) { + uint64_t t = static_cast((v > 0) ? v : -v); + int count = 0; + while ((t >>= 1) != 0) { + count++; + } + return count; +} + + +// TODO(srdjan): Clarify handling of negative right operand in a shift op. +static RawInteger* SmiShiftOperation(Token::Kind kind, + const Smi& left, + const Smi& right) { + ASSERT(right.Value() >= 0); + intptr_t result = 0; + switch (kind) { + case Token::kSHL: + if ((left.Value() == 0) || (right.Value() == 0)) { + return left.raw(); + } + { // Check for overflow. + int cnt = HighestBit(left.Value()); + if ((cnt + right.Value()) >= Smi::kBits) { + if ((cnt + right.Value()) >= Mint::kBits) { + return BigintOperations::ShiftLeft( + Bigint::Handle(AsBigint(left)), right.Value()); + } else { + int64_t left_64 = left.Value(); + return Integer::New(left_64 << right.Value()); + } + } + } + result = left.Value() << right.Value(); + break; + case Token::kSAR: { + int shift_amount = (right.Value() > 0x1F) ? 0x1F : right.Value(); + result = left.Value() >> shift_amount; + break; + } + default: + UNIMPLEMENTED(); + } + ASSERT(Smi::IsValid(result)); + return Smi::New(result); +} + + +static RawInteger* ShiftOperationHelper(Token::Kind kind, + const Integer& value, + const Smi& amount) { + if (amount.Value() < 0) { + // TODO(srdjan): Throw exception maybe. + UNIMPLEMENTED(); + } + if (value.IsSmi()) { + Smi& smi_value = Smi::Handle(); + smi_value ^= value.raw(); + return SmiShiftOperation(kind, smi_value, amount); + } + Bigint& big_value = Bigint::Handle(); + if (value.IsMint()) { + const int64_t mint_value = value.AsInt64Value(); + const int count = HighestBit(mint_value); + if ((count + amount.Value()) < Mint::kBits) { + switch (kind) { + case Token::kSHL: + return Integer::New(mint_value << amount.Value()); + case Token::kSAR: + return Integer::New(mint_value >> amount.Value()); + default: + UNIMPLEMENTED(); + } + } else { + // Overflow in shift, use Bigints + big_value = BigintOperations::NewFromInt64(mint_value); + } + } else { + ASSERT(value.IsBigint()); + big_value ^= value.raw(); + } + switch (kind) { + case Token::kSHL: + return BigintOperations::ShiftLeft(big_value, amount.Value()); + case Token::kSAR: + return BigintOperations::ShiftRight(big_value, amount.Value()); + default: + UNIMPLEMENTED(); + } + return Integer::null(); +} + + +DEFINE_NATIVE_ENTRY(Smi_sarFromInt, 2) { + const Smi& amount = Smi::CheckedHandle(arguments->At(0)); + const Integer& value = Integer::CheckedHandle(arguments->At(1)); + ASSERT(CheckInteger(amount)); + ASSERT(CheckInteger(value)); + Integer& result = Integer::Handle( + ShiftOperationHelper(Token::kSAR, value, amount)); + arguments->SetReturn(Integer::Handle(AsInteger(result))); +} + + + +DEFINE_NATIVE_ENTRY(Smi_shlFromInt, 2) { + const Smi& amount = Smi::CheckedHandle(arguments->At(0)); + const Integer& value = Integer::CheckedHandle(arguments->At(1)); + ASSERT(CheckInteger(amount)); + ASSERT(CheckInteger(value)); + if (FLAG_trace_intrinsified_natives) { + OS::Print("Smi_shlFromInt: %s << %s\n", + value.ToCString(), amount.ToCString()); + } + Integer& result = Integer::Handle( + ShiftOperationHelper(Token::kSHL, value, amount)); + arguments->SetReturn(Integer::Handle(AsInteger(result))); +} + + +DEFINE_NATIVE_ENTRY(Smi_bitNegate, 1) { + const Smi& operand = Smi::CheckedHandle(arguments->At(0)); + if (FLAG_trace_intrinsified_natives) { + OS::Print("Smi_bitNegate: %s\n", operand.ToCString()); + } + intptr_t result = ~operand.Value(); + ASSERT(Smi::IsValid(result)); + arguments->SetReturn(Smi::Handle(Smi::New(result))); +} + +// Mint natives. + +DEFINE_NATIVE_ENTRY(Mint_bitNegate, 1) { + const Mint& operand = Mint::CheckedHandle(arguments->At(0)); + ASSERT(CheckInteger(operand)); + if (FLAG_trace_intrinsified_natives) { + OS::Print("Mint_bitNegate: %s\n", operand.ToCString()); + } + int64_t result = ~operand.value(); + arguments->SetReturn(Integer::Handle(Integer::New(result))); +} + +// Bigint natives. + +DEFINE_NATIVE_ENTRY(Bigint_bitNegate, 1) { + const Bigint& value = Bigint::CheckedHandle(arguments->At(0)); + const Bigint& result = Bigint::Handle(BigintOperations::BitNot(value)); + ASSERT(CheckInteger(value)); + ASSERT(CheckInteger(result)); + arguments->SetReturn(Integer::Handle(AsInteger(result))); +} + +} // namespace dart diff --git a/runtime/lib/integers.dart b/runtime/lib/integers.dart new file mode 100644 index 00000000000..448a53ce925 --- /dev/null +++ b/runtime/lib/integers.dart @@ -0,0 +1,182 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(srdjan): fix limitations. +// - shift amount must be a Smi. +class IntegerImplementation { + num operator +(num other) { + return other.addFromInteger(this); + } + num operator -(num other) { + return other.subFromInteger(this); + } + num operator *(num other) { + return other.mulFromInteger(this); + } + num operator ~/(num other) { + if (other == 0) { + throw const IntegerDivisionByZeroException(); + } + return other.truncDivFromInteger(this); + } + num operator /(num other) { + return this.toDouble() / other.toDouble(); + } + num operator %(num other) { + if (other == 0) { + throw const IntegerDivisionByZeroException(); + } + return other.moduloFromInteger(this); + } + int operator negate() { + return 0 - this; + } + int operator &(int other) { + return other.bitAndFromInteger(this); + } + int operator |(int other) { + return other.bitOrFromInteger(this); + } + int operator ^(int other) { + return other.bitXorFromInteger(this); + } + num remainder(num other) { + return other.remainderFromInteger(this); + } + int bitAndFromInteger(int other) native "Integer_bitAndFromInteger"; + int bitOrFromInteger(int other) native "Integer_bitOrFromInteger"; + int bitXorFromInteger(int other) native "Integer_bitXorFromInteger"; + int addFromInteger(int other) native "Integer_addFromInteger"; + int subFromInteger(int other) native "Integer_subFromInteger"; + int mulFromInteger(int other) native "Integer_mulFromInteger"; + int truncDivFromInteger(int other) native "Integer_truncDivFromInteger"; + int moduloFromInteger(int other) native "Integer_moduloFromInteger"; + int remainderFromInteger(int other) { + return other - (other ~/ this) * this; + } + int operator >>(int other) { + return other.sarFromInt(this); + } + int operator <<(int other) { + return other.shlFromInt(this); + } + bool operator <(num other) { + return other > this; + } + bool operator >(num other) { + return other.greaterThanFromInteger(this); + } + bool operator >=(num other) { + return (this == other) || (this > other); + } + bool operator <=(num other) { + return (this == other) || (this < other); + } + bool greaterThanFromInteger(int other) + native "Integer_greaterThanFromInteger"; + bool operator ==(other) { + if (other is num) { + return other.equalToInteger(this); + } + return false; + } + bool equalToInteger(int other) native "Integer_equalToInteger"; + int abs() { + return this < 0 ? -this : this; + } + bool isEven() { return ((this & 1) === 0); } + bool isOdd() { return !isEven(); } + bool isNaN() { return false; } + bool isNegative() { return this < 0; } + bool isInfinite() { return false; } + + int compareTo(Comparable other) { + if (this == other) return 0; + if (this < other) return -1; + return 1; + } + int round() { return this; } + int floor() { return this; } + int ceil() { return this; } + int truncate() { return this; } + + int toInt() { return this; } + double toDouble() { return new Double.fromInteger(this); } + + int pow(int exponent) { + throw "IntegerImplementation.pow not implemented"; + } + + String toStringAsFixed(int fractionDigits) { + throw "IntegerImplementation.toStringAsFixed not implemented"; + } + String toStringAsExponential(int fractionDigits) { + throw "IntegerImplementation.toStringAsExponential not implemented"; + } + String toStringAsPrecision(int precision) { + throw "IntegerImplementation.toStringAsPrecision not implemented"; + } + String toRadixString(int radix) { + final table = const ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "A", "B", "C", "D", "E", "F"]; + if ((radix <= 1) || (radix > 16)) { + throw "Bad radix: $radix"; + } + final bool isNegative = this < 0; + var value = isNegative ? -this : this; + List temp = new List(); + while (value > 0) { + var digit = value % radix; + value ~/= radix; + temp.add(digit); + } + if (temp.isEmpty()) { + return "0"; + } + StringBuffer buffer = new StringBuffer(); + if (isNegative) buffer.add("-"); + for (int i = temp.length - 1; i >= 0; i--) { + buffer.add(table[temp[i]]); + } + return buffer.toString(); + } +} + +class Smi extends IntegerImplementation implements int { + int hashCode() { + return this; + } + int operator ~() native "Smi_bitNegate"; + int sarFromInt(int other) native "Smi_sarFromInt"; + int shlFromInt(int other) native "Smi_shlFromInt"; +} + +// Represents integers that cannot be represented by Smi but fit into 64bits. +class Mint extends IntegerImplementation implements int { + int hashCode() { + return this; + } + int operator ~() native "Mint_bitNegate"; +} + +// A number that can be represented as Smi or Mint will never be represented as +// Bigint. +class Bigint extends IntegerImplementation implements int { + int hashCode() { + return this; + } + int operator ~() native "Bigint_bitNegate"; + + // Shift by bigint exceeds range that can be handled by the VM. + int sarFromInt(int other) { + if (other < 0) { + return -1; + } else { + return 0; + } + } + int shlFromInt(int other) { + throw const OutOfMemoryException(); + } +} diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc new file mode 100644 index 00000000000..45fd7673e48 --- /dev/null +++ b/runtime/lib/isolate.cc @@ -0,0 +1,378 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bootstrap_natives.h" + +#include "vm/assert.h" +#include "vm/class_finalizer.h" +#include "vm/dart.h" +#include "vm/dart_entry.h" +#include "vm/exceptions.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/port.h" +#include "vm/resolver.h" +#include "vm/snapshot.h" +#include "vm/thread.h" + +namespace dart { + +class IsolateStartData { + public: + IsolateStartData(Isolate* isolate, + char* library_url, + char* class_name, + intptr_t port_id) + : isolate_(isolate), + library_url_(library_url), + class_name_(class_name), + port_id_(port_id) {} + + Isolate* isolate_; + char* library_url_; + char* class_name_; + intptr_t port_id_; +}; + + +static RawInstance* DeserializeMessage(void* data) { + // Create a snapshot object using the buffer. + Snapshot* snapshot = Snapshot::SetupFromBuffer(data); + ASSERT(snapshot->IsPartialSnapshot()); + + // Read object back from the snapshot. + Isolate* isolate= Isolate::Current(); + SnapshotReader reader(snapshot, isolate->heap(), isolate->object_store()); + Instance& instance = Instance::Handle(); + instance ^= reader.ReadObject(); + return instance.raw(); +} + + +static uint8_t* allocator(uint8_t* ptr, intptr_t old_size, intptr_t new_size) { + void* new_ptr = realloc(reinterpret_cast(ptr), new_size); + return reinterpret_cast(new_ptr); +} + + +static void* SerializeObject(const Instance& obj) { + uint8_t* result = NULL; + SnapshotWriter writer(false, &result, &allocator); + writer.WriteObject(obj.raw()); + writer.FinalizeBuffer(); + return result; +} + + +static void ProcessUnhandledException(const UnhandledException& uhe) { + const Instance& exception = Instance::Handle(uhe.exception()); + Instance& string = Instance::Handle(DartLibraryCalls::ToString(exception)); + const char* str = string.ToCString(); + fprintf(stderr, "%s\n", str); + const Instance& stack = Instance::Handle(uhe.stacktrace()); + string = DartLibraryCalls::ToString(stack); + str = string.ToCString(); + fprintf(stderr, "%s\n", str); + exit(255); +} + + +static void ThrowErrorException(Exceptions::ExceptionType type, + const char* error_msg, + const char* library_url, + const char* class_name) { + String& str = String::Handle(); + String& name = String::Handle(); + str ^= String::New(error_msg); + name ^= String::NewSymbol(library_url); + str ^= String::Concat(str, name); + name ^= String::New(":"); + str ^= String::Concat(str, name); + name ^= String::NewSymbol(class_name); + str ^= String::Concat(str, name); + GrowableArray arguments(1); + arguments.Add(&str); + Exceptions::ThrowByType(type, arguments); +} + + +RawInstance* ReceivePortCreate(intptr_t port_id) { + const String& class_name = + String::Handle(String::NewSymbol("ReceivePortImpl")); + const String& function_name = String::Handle(String::NewSymbol("create_")); + const int kNumArguments = 1; + const Array& kNoArgumentNames = Array::Handle(); + const Function& function = Function::Handle( + Resolver::ResolveStatic(Library::Handle(Library::CoreLibrary()), + class_name, + function_name, + kNumArguments, + kNoArgumentNames, + Resolver::kIsQualified)); + GrowableArray arguments(kNumArguments); + arguments.Add(&Integer::Handle(Integer::New(port_id))); + const Instance& result = Instance::Handle( + DartEntry::InvokeStatic(function, arguments)); + if (result.IsUnhandledException()) { + UnhandledException& uhe = UnhandledException::Handle(); + uhe ^= result.raw(); + ProcessUnhandledException(uhe); + } + return result.raw(); +} + + +static RawInstance* SendPortCreate(intptr_t port_id) { + const String& class_name = String::Handle(String::NewSymbol("SendPortImpl")); + const String& function_name = String::Handle(String::NewSymbol("create_")); + const int kNumArguments = 1; + const Array& kNoArgumentNames = Array::Handle(); + const Function& function = Function::Handle( + Resolver::ResolveStatic(Library::Handle(Library::CoreLibrary()), + class_name, + function_name, + kNumArguments, + kNoArgumentNames, + Resolver::kIsQualified)); + GrowableArray arguments(kNumArguments); + arguments.Add(&Integer::Handle(Integer::New(port_id))); + const Instance& result = Instance::Handle( + DartEntry::InvokeStatic(function, arguments)); + return result.raw(); +} + + +void PortMessage::Handle() { + const Instance& msg = Instance::Handle(DeserializeMessage(data())); + const String& class_name = + String::Handle(String::NewSymbol("ReceivePortImpl")); + const String& function_name = + String::Handle(String::NewSymbol("handleMessage_")); + const int kNumArguments = 3; + const Array& kNoArgumentNames = Array::Handle(); + const Function& function = Function::Handle( + Resolver::ResolveStatic(Library::Handle(Library::CoreLibrary()), + class_name, + function_name, + kNumArguments, + kNoArgumentNames, + Resolver::kIsQualified)); + GrowableArray arguments(kNumArguments); + arguments.Add(&Integer::Handle(Integer::New(dest_id()))); + arguments.Add(&Integer::Handle(Integer::New(reply_id()))); + arguments.Add(&msg); + const Object& result = Object::Handle( + DartEntry::InvokeStatic(function, arguments)); + if (result.IsUnhandledException()) { + UnhandledException& uhe = UnhandledException::Handle(); + uhe ^= result.raw(); + ProcessUnhandledException(uhe); + } + ASSERT(result.IsNull()); +} + + +static void RunIsolate(uword parameter) { + IsolateStartData* data = reinterpret_cast(parameter); + Isolate* isolate = data->isolate_; + char* library_url = data->library_url_; + char* class_name = data->class_name_; + intptr_t port_id = data->port_id_; + delete data; + + Isolate::SetCurrent(isolate); + // Intialize stack limit in case we are running isolate in a + // different thread than in which it was initialized. + isolate->SetStackLimitFromCurrentTOS(reinterpret_cast(&isolate)); + + { + Zone zone; + HandleScope handle_scope; + ASSERT(ClassFinalizer::FinalizePendingClasses()); + // Lookup the target class by name, create an instance and call the run + // method. + const String& lib_name = String::Handle(String::NewSymbol(library_url)); + const Library& lib = Library::Handle(Library::LookupLibrary(lib_name)); + ASSERT(!lib.IsNull()); + const String& cls_name = String::Handle(String::NewSymbol(class_name)); + const Class& target_class = Class::Handle(lib.LookupClass(cls_name)); + // TODO(iposva): Deserialize or call the constructor after allocating. + // For now, we only support a non-parameterized or raw target class. + const Instance& target = Instance::Handle(Instance::New(target_class)); + Instance& result = Instance::Handle(); + + // Invoke the default constructor. + const String& period = String::Handle(String::New(".")); + String& constructor_name = String::Handle(String::Concat(cls_name, period)); + const Function& default_constructor = + Function::Handle(target_class.LookupConstructor(constructor_name)); + if (!default_constructor.IsNull()) { + GrowableArray arguments(1); + arguments.Add(&target); + result = DartEntry::InvokeStatic(default_constructor, arguments); + if (result.IsUnhandledException()) { + UnhandledException& uhe = UnhandledException::Handle(); + uhe ^= result.raw(); + ProcessUnhandledException(uhe); + } + ASSERT(result.IsNull()); + } + + // Invoke the "_run" method. + const Function& target_function = Function::Handle(Resolver::ResolveDynamic( + target, String::Handle(String::NewSymbol("_run")), 2, 0)); + // TODO(iposva): Proper error checking here. + ASSERT(!target_function.IsNull()); + // TODO(iposva): Allocate the proper port number here. + const Instance& local_port = Instance::Handle(ReceivePortCreate(port_id)); + GrowableArray arguments(1); + arguments.Add(&local_port); + result = DartEntry::InvokeDynamic(target, target_function, arguments); + if (result.IsUnhandledException()) { + UnhandledException& uhe = UnhandledException::Handle(); + uhe ^= result.raw(); + ProcessUnhandledException(uhe); + } + ASSERT(result.IsNull()); + } + free(class_name); + + // Keep listening until there are no active receive ports. + while (isolate->active_ports() > 0) { + Zone zone; + HandleScope handle_scope; + + PortMessage* message = PortMap::ReceiveMessage(0); + message->Handle(); + delete message; + } + + Dart::ShutdownIsolate(); +} + + +static bool CheckArguments(const char* library_url, const char* class_name) { + Zone zone; + HandleScope handle_scope; + String& name = String::Handle(); + if (!ClassFinalizer::FinalizePendingClasses()) { + return false; + } + // Lookup the target class by name, create an instance and call the run + // method. + name ^= String::NewSymbol(library_url); + const Library& lib = Library::Handle(Library::LookupLibrary(name)); + if (lib.IsNull()) { + const String& error = String::Handle( + String::New("Error starting Isolate, library not loaded : ")); + Isolate::Current()->object_store()->set_sticky_error(error); + return false; + } + name ^= String::NewSymbol(class_name); + const Class& target_class = Class::Handle(lib.LookupClass(name)); + if (target_class.IsNull()) { + const String& error = String::Handle( + String::New("Error starting Isolate, class not loaded : ")); + Isolate::Current()->object_store()->set_sticky_error(error); + return false; + } + return true; // No errors. +} + + +DEFINE_NATIVE_ENTRY(IsolateNatives_start, 2) { + Isolate* preserved_isolate = Isolate::Current(); + const Instance& runnable = Instance::CheckedHandle(arguments->At(0)); + const Class& runnable_class = Class::Handle(runnable.clazz()); + const char* class_name = String::Handle(runnable_class.Name()).ToCString(); + const Library& library = Library::Handle(runnable_class.library()); + ASSERT(!library.IsNull()); + const char* library_url = String::Handle(library.url()).ToCString(); + + intptr_t port_id = 0; + const char* error_msg = NULL; + + Isolate* spawned_isolate = + Dart::CreateIsolate(NULL, preserved_isolate->init_callback_data()); + if (spawned_isolate != NULL) { + // Check arguments to see if the specified library and classes are + // loaded, this check will throw an exception if they are not loaded. + if (CheckArguments(library_url, class_name)) { + port_id = PortMap::CreatePort(); + uword data = reinterpret_cast( + new IsolateStartData(spawned_isolate, + strdup(library_url), + strdup(class_name), + port_id)); + new Thread(RunIsolate, data); + } else { + // Error loading application into spawned isolate, shut it down and + // report error. + // Make sure to grab the error message out of the isolate before it has + // been shutdown and to allocate it in the preserved isolates zone. + { + Zone zone; + HandleScope scope; + const String& error = String::Handle( + spawned_isolate->object_store()->sticky_error()); + const char* temp_error_msg = error.ToCString(); + intptr_t err_len = strlen(temp_error_msg) + 1; + Zone* preserved_zone = preserved_isolate->current_zone(); + error_msg = reinterpret_cast(preserved_zone->Allocate(err_len)); + OS::SNPrint( + const_cast(error_msg), err_len, "%s", temp_error_msg); + } + Dart::ShutdownIsolate(); + spawned_isolate = NULL; + } + } else { + error_msg = "Creation of Isolate failed : "; + } + + // Switch back to the original isolate and return. + Isolate::SetCurrent(preserved_isolate); + if (spawned_isolate == NULL) { + // Unable to spawn isolate correctly, throw exception. + ASSERT(error_msg != NULL); + ThrowErrorException(Exceptions::kIllegalArgument, + error_msg, + library_url, + class_name); + } + const Instance& port = Instance::Handle(SendPortCreate(port_id)); + if (port.IsUnhandledException()) { + ThrowErrorException(Exceptions::kInternalError, + "Unable to create send port to isolate", + library_url, + class_name); + } + arguments->SetReturn(port); +} + + +DEFINE_NATIVE_ENTRY(ReceivePortImpl_factory, 1) { + ASSERT(TypeArguments::CheckedHandle(arguments->At(0)).IsNull()); + intptr_t port_id = PortMap::CreatePort(); + const Instance& port = Instance::Handle(ReceivePortCreate(port_id)); + arguments->SetReturn(port); +} + + +DEFINE_NATIVE_ENTRY(ReceivePortImpl_closeInternal, 1) { + intptr_t id = Smi::CheckedHandle(arguments->At(0)).Value(); + PortMap::ClosePort(id); +} + + +DEFINE_NATIVE_ENTRY(SendPortImpl_sendInternal_, 3) { + intptr_t send_id = Smi::CheckedHandle(arguments->At(0)).Value(); + intptr_t reply_id = Smi::CheckedHandle(arguments->At(1)).Value(); + // TODO(iposva): Allow for arbitrary messages to be sent. + void* data = SerializeObject(Instance::CheckedHandle(arguments->At(2))); + + PortMessage* message = new PortMessage(send_id, reply_id, data); + PortMap::PostMessage(message); +} + +} // namespace dart diff --git a/runtime/lib/isolate.dart b/runtime/lib/isolate.dart new file mode 100644 index 00000000000..0c7625c64b6 --- /dev/null +++ b/runtime/lib/isolate.dart @@ -0,0 +1,166 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class ReceivePortFactory { + factory ReceivePort() { + return new ReceivePortImpl(); + } + + factory ReceivePort.singleShot() { + return new ReceivePortSingleShotImpl(); + } +} + + +class ReceivePortImpl implements ReceivePort { + /*--- public interface ---*/ + factory ReceivePortImpl() native "ReceivePortImpl_factory"; + + receive(void onMessage(var message, SendPort replyTo)) { + _onMessage = onMessage; + } + + close() { + _portMap.remove(_id); + _closeInternal(_id); + } + + SendPort toSendPort() { + return new SendPortImpl(_id); + } + + /**** Internal implementation details ****/ + // Called from the VM to create a new ReceivePort instance. + static ReceivePortImpl create_(int id) { + return new ReceivePortImpl._internal(id); + } + ReceivePortImpl._internal(int id) : _id = id { + if (_portMap === null) { + _portMap = new Map(); + } + _portMap[id] = this; + } + + // Called from the VM to dispatch to the handler. + static void handleMessage_(int id, int replyId, var message) { + assert(_portMap != null); + ReceivePort port = _portMap[id]; + SendPort replyTo = (replyId == 0) ? null : new SendPortImpl(replyId); + (port._onMessage)(message, replyTo); + PromiseQueue.process(); + } + + // Call into the VM to close the VM maintained mappings. + static _closeInternal(int id) native "ReceivePortImpl_closeInternal"; + + final int _id; + var _onMessage; + + // id to ReceivePort mapping. + static Map _portMap; +} + + +class ReceivePortSingleShotImpl implements ReceivePort { + + ReceivePortSingleShotImpl() : _port = new ReceivePortImpl() { } + + void receive(void callback(var message, SendPort replyTo)) { + _port.receive((var message, SendPort replyTo) { + _port.close(); + callback(message, replyTo); + }); + } + + void close() { + _port.close(); + } + + SendPort toSendPort() { + return _port.toSendPort(); + } + + final ReceivePortImpl _port; + +} + + +class SendPortImpl implements SendPort { + /*--- public interface ---*/ + void send(var message, SendPort replyTo) { + if (PromiseQueue.isEmpty()) { + this._sendNow(message, replyTo); + } else { + _enqueueSend(message, replyTo); + } + } + + void _enqueueSend(var message, SendPort replyTo) { + PromiseQueue.enqueue(const []).then((ignored) { + this._sendNow(message, replyTo); + }); + } + + void _sendNow(var message, SendPort replyTo) { + int replyId = (replyTo === null) ? 0 : replyTo._id; + _sendInternal(_id, replyId, message); + } + + ReceivePortSingleShotImpl call(var message) { + final result = new ReceivePortSingleShotImpl(); + this.send(message, result.toSendPort()); + return result; + } + + ReceivePortSingleShotImpl _callNow(var message) { + final result = new ReceivePortSingleShotImpl(); + this._sendNow(message, result.toSendPort()); + return result; + } + + bool operator==(var other) { + return (other is SendPortImpl) && _id == other._id; + } + + int hashCode() { + return _id; + } + + /*--- private implementation ---*/ + const SendPortImpl(int id) : _id = id; + + // SendPortImpl.create_ is called from the VM when a new SendPort instance is + // needed by the VM code. + static SendPort create_(int id) { + return new SendPortImpl(id); + } + + // Forward the implementation of sending messages to the VM. Only port ids + // are being handed to the VM. + static _sendInternal(int sendId, int replyId, var message) + native "SendPortImpl_sendInternal_"; + + final int _id; +} + + +class IsolateNatives { + static Promise spawn(Isolate isolate, bool isLight) { + Promise result = new Promise(); + SendPort port = _start(isolate, isLight); + result.complete(port); + return result; + } + + // Starts a new isolate calling the run method on a new instance of the + // remote class's type. + // Returns the send port which is passed to the newly created isolate. + // This method is being dispatched to from the public core library code. + static SendPort _start(Isolate isolate, bool light) + native "IsolateNatives_start"; + + // The VM knows in which isolate the function must be run, therefore + // there is no need for wrapping it. + static Function bind(Function f) { return f; } +} diff --git a/runtime/lib/lib_impl_sources.gypi b/runtime/lib/lib_impl_sources.gypi new file mode 100644 index 00000000000..200f43d9d0c --- /dev/null +++ b/runtime/lib/lib_impl_sources.gypi @@ -0,0 +1,34 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +# Implementation sources. + +{ + 'sources': [ + 'array.cc', + 'array.dart', + 'arrays.dart', + 'bool.dart', + 'collections.dart', + 'date_time.cc', + 'date_time.dart', + 'double.cc', + 'double.dart', + 'growable_array.dart', + 'immutable_map.dart', + 'integers.cc', + 'integers.dart', + 'isolate.cc', + 'isolate.dart', + 'math.dart', + 'math.cc', + 'regexp.cc', + 'regexp_jsc.cc', + 'regexp_jsc.h', + 'regexp.dart', + 'string.cc', + 'string.dart', + 'string_buffer.dart', + ], +} diff --git a/runtime/lib/lib_sources.gypi b/runtime/lib/lib_sources.gypi new file mode 100644 index 00000000000..48ee7791001 --- /dev/null +++ b/runtime/lib/lib_sources.gypi @@ -0,0 +1,17 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +# Sources visible to via default library. + +{ + 'sources': [ + 'clock.cc', + 'clock.dart', + 'error.cc', + 'error.dart', + 'error.h', + 'object.cc', + 'object.dart', + ], +} diff --git a/runtime/lib/math.cc b/runtime/lib/math.cc new file mode 100644 index 00000000000..8932659636b --- /dev/null +++ b/runtime/lib/math.cc @@ -0,0 +1,183 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include // isspace. + +#include "vm/bootstrap_natives.h" + +#include "vm/bigint_operations.h" +#include "vm/exceptions.h" +#include "vm/native_entry.h" +#include "vm/object.h" +#include "vm/random.h" +#include "vm/scanner.h" + +namespace dart { + +DEFINE_NATIVE_ENTRY(MathNatives_sqrt, 1) { + const double operand = Double::CheckedHandle(arguments->At(0)).value(); + arguments->SetReturn(Double::Handle(Double::New(sqrt(operand)))); +} + +DEFINE_NATIVE_ENTRY(MathNatives_sin, 1) { + const double operand = Double::CheckedHandle(arguments->At(0)).value(); + arguments->SetReturn(Double::Handle(Double::New(sin(operand)))); +} + +DEFINE_NATIVE_ENTRY(MathNatives_cos, 1) { + const double operand = Double::CheckedHandle(arguments->At(0)).value(); + arguments->SetReturn(Double::Handle(Double::New(cos(operand)))); +} + +DEFINE_NATIVE_ENTRY(MathNatives_tan, 1) { + const double operand = Double::CheckedHandle(arguments->At(0)).value(); + arguments->SetReturn(Double::Handle(Double::New(tan(operand)))); +} + +DEFINE_NATIVE_ENTRY(MathNatives_asin, 1) { + const double operand = Double::CheckedHandle(arguments->At(0)).value(); + arguments->SetReturn(Double::Handle(Double::New(asin(operand)))); +} + +DEFINE_NATIVE_ENTRY(MathNatives_acos, 1) { + const double operand = Double::CheckedHandle(arguments->At(0)).value(); + arguments->SetReturn(Double::Handle(Double::New(acos(operand)))); +} + +DEFINE_NATIVE_ENTRY(MathNatives_atan, 1) { + const double operand = Double::CheckedHandle(arguments->At(0)).value(); + arguments->SetReturn(Double::Handle(Double::New(atan(operand)))); +} + +// It is not possible to call the native MathNatives_atan2. Somehow this leads +// to a dynamic error "native function 'MathNatives_atan2' cannot be found". +DEFINE_NATIVE_ENTRY(MathNatives_2atan, 2) { + const double operand1 = Double::CheckedHandle(arguments->At(0)).value(); + const double operand2 = Double::CheckedHandle(arguments->At(1)).value(); + arguments->SetReturn(Double::Handle(Double::New(atan2(operand1, operand2)))); +} + +DEFINE_NATIVE_ENTRY(MathNatives_exp, 1) { + const double operand = Double::CheckedHandle(arguments->At(0)).value(); + arguments->SetReturn(Double::Handle(Double::New(exp(operand)))); +} + +DEFINE_NATIVE_ENTRY(MathNatives_log, 1) { + const double operand = Double::CheckedHandle(arguments->At(0)).value(); + arguments->SetReturn(Double::Handle(Double::New(log(operand)))); +} + +DEFINE_NATIVE_ENTRY(MathNatives_random, 0) { + arguments->SetReturn(Double::Handle(Double:: + New(static_cast(Random::RandomInt32()-1)/0x80000000))); +} + + +// TODO(srdjan): Investigate for performance hit; the integer and double parsing +// may not be efficient as we need to generate two extra growable arrays. +static bool IsValidLiteral(const Scanner::GrowableTokenStream& tokens, + Token::Kind literal_kind, + bool* is_positive, + String** value) { + if ((tokens.length() == 2) && + (tokens[0].kind == literal_kind) && + (tokens[1].kind == Token::kEOS)) { + *is_positive = true; + *value = tokens[0].literal; + return true; + } + if ((tokens.length() == 3) && + ((tokens[0].kind == Token::kADD) || (tokens[0].kind == Token::kSUB)) && + (tokens[1].kind == literal_kind) && + (tokens[2].kind == Token::kEOS)) { + // Check there is no space between "+/-" and number. + if ((tokens[0].offset + 1) != tokens[1].offset) { + return false; + } + *is_positive = tokens[0].kind == Token::kADD; + *value = tokens[1].literal; + return true; + } + return false; +} + + +DEFINE_NATIVE_ENTRY(MathNatives_parseInt, 1) { + const String& value = String::CheckedHandle(arguments->At(0)); + Scanner scanner(value, String::Handle()); + const Scanner::GrowableTokenStream& tokens = scanner.GetStream(); + String* int_string; + bool is_positive; + if (IsValidLiteral(tokens, Token::kINTEGER, &is_positive, &int_string)) { + Integer& result = Integer::Handle(); + if (is_positive) { + result = Integer::New(*int_string); + } else { + String& temp = String::Handle(); + temp = String::Concat(String::Handle(String::NewSymbol("-")), + *int_string); + result = Integer::New(temp); + } + arguments->SetReturn(result); + } else { + GrowableArray args; + args.Add(&value); + Exceptions::ThrowByType(Exceptions::kBadNumberFormat, args); + } +} + + +DEFINE_NATIVE_ENTRY(MathNatives_parseDouble, 1) { + const String& value = String::CheckedHandle(arguments->At(0)); + Scanner scanner(value, String::Handle()); + const Scanner::GrowableTokenStream& tokens = scanner.GetStream(); + String* number_string; + bool is_positive; + if (IsValidLiteral(tokens, Token::kDOUBLE, &is_positive, &number_string)) { + const char* cstr = number_string->ToCString(); + char* p_end = NULL; + double double_value = strtod(cstr, &p_end); + ASSERT(p_end != cstr); + if (!is_positive) { + double_value = -double_value; + } + Double& result = Double::Handle(Double::New(double_value)); + arguments->SetReturn(result); + return; + } + + if (IsValidLiteral(tokens, Token::kINTEGER, &is_positive, &number_string)) { + Integer& res = Integer::Handle(Integer::New(*number_string)); + if (is_positive) { + arguments->SetReturn(Double::Handle(Double::New(res.AsDoubleValue()))); + } else { + arguments->SetReturn(Double::Handle(Double::New(-res.AsDoubleValue()))); + } + return; + } + + // Infinity and nan. + if (IsValidLiteral(tokens, Token::kIDENT, &is_positive, &number_string)) { + const char* kNan = "NaN"; + const char* kInfinity = "Infinity"; + if (number_string->Equals(kNan, strlen(kNan))) { + arguments->SetReturn(Double::Handle(Double::New(NAN))); + return; + } + if (number_string->Equals(kInfinity, strlen(kInfinity))) { + if (is_positive) { + arguments->SetReturn(Double::Handle(Double::New(INFINITY))); + } else { + arguments->SetReturn(Double::Handle(Double::New(-INFINITY))); + } + return; + } + } + + GrowableArray args; + args.Add(&value); + Exceptions::ThrowByType(Exceptions::kBadNumberFormat, args); +} + +} // namespace dart diff --git a/runtime/lib/math.dart b/runtime/lib/math.dart new file mode 100644 index 00000000000..b42b04585ce --- /dev/null +++ b/runtime/lib/math.dart @@ -0,0 +1,78 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class MathNatives { + static int parseInt(String str) { + if (str is !String) { + throw "Wrong argument"; + } + return _parseInt(str); + } + static double parseDouble(String str) { + if (str is !String) { + throw "Wrong argument"; + } + return _parseDouble(str); + } + static double sqrt(num value) { + return _sqrt(_asDouble(value)); + } + static double sin(num value) { + return _sin(_asDouble(value)); + } + static double cos(num value) { + return _cos(_asDouble(value)); + } + static double tan(num value) { + return _tan(_asDouble(value)); + } + static double acos(num value) { + return _acos(_asDouble(value)); + } + static double asin(num value) { + return _asin(_asDouble(value)); + } + static double atan(num value) { + return _atan(_asDouble(value)); + } + static double atan2(num a, num b) { + return _atan2(_asDouble(a), _asDouble(b)); + } + static double exp(num value) { + return _exp(_asDouble(value)); + } + static double log(num value) { + return _log(_asDouble(value)); + } + static num pow(num value, num exponent) { + if (exponent is int) { + return value.pow(exponent); + } + // Double.pow will call exponent.toDouble(). + return _asDouble(value).pow(exponent); + } + static double random() { + return _random(); + } + static double _asDouble(num value) { + double result = value.toDouble(); + if (result is !double) { + throw "Wrong argument"; + } + return result; + } + static double _random() native "MathNatives_random"; + static double _sqrt(double value) native "MathNatives_sqrt"; + static double _sin(double value) native "MathNatives_sin"; + static double _cos(double value) native "MathNatives_cos"; + static double _tan(double value) native "MathNatives_tan"; + static double _acos(double value) native "MathNatives_acos"; + static double _asin(double value) native "MathNatives_asin"; + static double _atan(double value) native "MathNatives_atan"; + static double _atan2(double a, double b) native "MathNatives_2atan"; + static double _exp(double value) native "MathNatives_exp"; + static double _log(double value) native "MathNatives_log"; + static int _parseInt(String str) native "MathNatives_parseInt"; + static double _parseDouble(String str) native "MathNatives_parseDouble"; +} diff --git a/runtime/lib/object.cc b/runtime/lib/object.cc new file mode 100644 index 00000000000..b94680de959 --- /dev/null +++ b/runtime/lib/object.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bootstrap_natives.h" + +#include "vm/exceptions.h" +#include "vm/native_entry.h" +#include "vm/object.h" + +namespace dart { + +DEFINE_NATIVE_ENTRY(Object_toString, 1) { + const Instance& instance = Instance::CheckedHandle(arguments->At(0)); + const char* c_str = instance.ToCString(); + arguments->SetReturn(String::Handle(String::New(c_str))); +} + + +DEFINE_NATIVE_ENTRY(Object_noSuchMethod, 3) { + const Instance& instance = Instance::CheckedHandle(arguments->At(0)); + const String& function_name = String::CheckedHandle(arguments->At(1)); + const Array& func_args = Array::CheckedHandle(arguments->At(2)); + if (instance.IsNull()) { + GrowableArray args; + Exceptions::ThrowByType(Exceptions::kNullPointer, args); + } + GrowableArray dart_arguments(3); + dart_arguments.Add(&instance); + dart_arguments.Add(&function_name); + dart_arguments.Add(&func_args); + Exceptions::ThrowByType(Exceptions::kNoSuchMethod, dart_arguments); +} + +} // namespace dart diff --git a/runtime/lib/object.dart b/runtime/lib/object.dart new file mode 100644 index 00000000000..e056dc9439d --- /dev/null +++ b/runtime/lib/object.dart @@ -0,0 +1,17 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class Object { + const Object(); + String toString() native "Object_toString"; + bool operator ==(other) { + return this === other; + } + void noSuchMethod(String function_name, Array args) native "Object_noSuchMethod"; + + /** + * Return this object without type information. + */ + get dynamic() { return this; } +} diff --git a/runtime/lib/regexp.cc b/runtime/lib/regexp.cc new file mode 100644 index 00000000000..eb0fec6f60d --- /dev/null +++ b/runtime/lib/regexp.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bootstrap_natives.h" + +#include "vm/assert.h" +#include "vm/exceptions.h" +#include "vm/native_entry.h" +#include "vm/object.h" + +#include "lib/regexp_jsc.h" + +namespace dart { + +DEFINE_NATIVE_ENTRY(JSSyntaxRegExp_factory, 3) { + ASSERT(TypeArguments::CheckedHandle(arguments->At(0)).IsNull()); + const String& pattern = String::CheckedHandle(arguments->At(1)); + const String& flags = String::CheckedHandle(arguments->At(2)); + const JSRegExp& new_regex = JSRegExp::Handle(Jscre::Compile(pattern, flags)); + arguments->SetReturn(new_regex); +} + + +DEFINE_NATIVE_ENTRY(JSSyntaxRegExp_getPattern, 1) { + const JSRegExp& regexp = JSRegExp::CheckedHandle(arguments->At(0)); + const String& result = String::Handle(regexp.pattern()); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(JSSyntaxRegExp_getFlags, 1) { + const JSRegExp& regexp = JSRegExp::CheckedHandle(arguments->At(0)); + const String& result = String::Handle(String::New(regexp.Flags())); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(JSSyntaxRegExp_getGroupCount, 1) { + const JSRegExp& regexp = JSRegExp::CheckedHandle(arguments->At(0)); + if (regexp.is_initialized()) { + const Smi& result = Smi::Handle(regexp.num_bracket_expressions()); + arguments->SetReturn(result); + return; + } + const String& pattern = String::Handle(regexp.pattern()); + const String& errmsg = + String::Handle(String::New("Regular expression is not initialized yet")); + GrowableArray args; + args.Add(&pattern); + args.Add(&errmsg); + Exceptions::ThrowByType(Exceptions::kIllegalJSRegExp, args); +} + + +DEFINE_NATIVE_ENTRY(JSSyntaxRegExp_ExecuteMatch, 3) { + const JSRegExp& regexp = JSRegExp::CheckedHandle(arguments->At(0)); + const String& str = String::CheckedHandle(arguments->At(1)); + const Smi& start_index = Smi::CheckedHandle(arguments->At(2)); + const Array& result = + Array::Handle(Jscre::Execute(regexp, str, start_index.Value())); + arguments->SetReturn(result); +} + +} // namespace dart diff --git a/runtime/lib/regexp.dart b/runtime/lib/regexp.dart new file mode 100644 index 00000000000..59f50c7ee5c --- /dev/null +++ b/runtime/lib/regexp.dart @@ -0,0 +1,101 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class JSRegExpMatch implements Match { + JSRegExpMatch(this.regexp, this.str, this._match); + + int start() { + return _start(0); + } + + int end() { + return _end(0); + } + + int _start(int group) { + return _match[(group * _kMatchPair)]; + } + + int _end(int group) { + return _match[(group * _kMatchPair) + 1]; + } + + String group(int group) { + return str.substringUnchecked_(_start(group), _end(group)); + } + + String operator [](int group) { + return str.substringUnchecked_(_start(group), _end(group)); + } + + List groups(List groups) { + var groupsList = new List(groups.length); + for (int i = 0; i < groups.length; i++) { + int grp_idx = groups[i]; + groupsList[i] = str.substringUnchecked_(_start(grp_idx), _end(grp_idx)); + } + return groupsList; + } + + int groupCount() { + return regexp._groupCount; + } + + final RegExp regexp; + final String str; + final List _match; + static final int _kMatchPair = 2; +} + + +class JSSyntaxRegExp implements RegExp { + const factory JSSyntaxRegExp(String pattern, String flags) + native "JSSyntaxRegExp_factory"; + + Match firstMatch(String str) { + List match = _ExecuteMatch(str, 0); + if (match === null) { + return null; + } + return new JSRegExpMatch(this, str, match); + } + + Iterable allMatches(String str) { + var jsregexMatches = new GrowableObjectArray(); + List match = _ExecuteMatch(str, 0); + if (match !== null) { + jsregexMatches.add(new JSRegExpMatch(this, str, match)); + while (true) { + match = _ExecuteMatch(str, match[1]); + if (match === null) { + break; + } + jsregexMatches.add(new JSRegExpMatch(this, str, match)); + } + } + return jsregexMatches; + } + + bool hasMatch(String str) { + List match = _ExecuteMatch(str, 0); + return (match === null) ? false : true; + } + + String stringMatch(String str) { + List match = _ExecuteMatch(str, 0); + if (match === null) { + return null; + } + return str.substringUnchecked_(match[0], match[1]); + } + + String get pattern() native "JSSyntaxRegExp_getPattern"; + + String get flags() native "JSSyntaxRegExp_getFlags"; + + int get _groupCount() native "JSSyntaxRegExp_getGroupCount"; + + List _ExecuteMatch(String str, int start_index) + native "JSSyntaxRegExp_ExecuteMatch"; +} diff --git a/runtime/lib/regexp_jsc.cc b/runtime/lib/regexp_jsc.cc new file mode 100644 index 00000000000..d208327f110 --- /dev/null +++ b/runtime/lib/regexp_jsc.cc @@ -0,0 +1,187 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// This file encapsulates all the interaction with the +// JSC regular expression library also referred to as pcre + +#include "lib/regexp_jsc.h" + +#include "vm/allocation.h" +#include "vm/assert.h" +#include "vm/exceptions.h" +#include "vm/globals.h" +#include "vm/isolate.h" + +#include "third_party/jscre/pcre.h" + +namespace dart { + +static uint16_t* GetTwoByteData(const String& str) { + intptr_t size = str.Length() * sizeof(uint16_t); + Zone* zone = Isolate::Current()->current_zone(); + uint16_t* two_byte_str = reinterpret_cast(zone->Allocate(size)); + for (intptr_t i = 0; i < str.Length(); i++) { + two_byte_str[i] = str.CharAt(i); + } + return two_byte_str; +} + + +static void* JSREMalloc(size_t size) { + intptr_t regexp_size = static_cast(size); + ASSERT(regexp_size > 0); + const JSRegExp& new_regex = JSRegExp::Handle(JSRegExp::New(size)); + return new_regex.GetDataStartAddress(); +} + + +static void JSREFree(void* ptr) { + USE(ptr); // Do nothing, memory is garbage collected. +} + + +static void ThrowExceptionOnError(const String& pattern, + const char* error_msg) { + if (error_msg == NULL) { + error_msg = "Unknown regexp compile error"; + } + const String& errmsg = String::Handle(String::New(error_msg)); + GrowableArray args; + args.Add(&pattern); + args.Add(&errmsg); + Exceptions::ThrowByType(Exceptions::kIllegalJSRegExp, args); +} + + +RawJSRegExp* Jscre::Compile(const String& pattern, const String& flags) { + // First convert the pattern to UTF16 format as the jscre library expects + // strings to be in UTF16 encoding. + uint16_t* two_byte_pattern = GetTwoByteData(pattern); + + // Parse the flags. + jscre::JSRegExpIgnoreCaseOption ignore_case = jscre::JSRegExpDoNotIgnoreCase; + // A Dart regexp is always global. + bool is_global = true; + jscre::JSRegExpMultilineOption multi_line = jscre::JSRegExpSingleLine; + for (int i = 0; i < flags.Length(); i++) { + switch (flags.CharAt(i)) { + case 'i': + ignore_case = jscre::JSRegExpIgnoreCase; + break; + case 'm': + multi_line = jscre::JSRegExpMultiline; + break; + default: + // Unrecognized flag, throw an exception. + ThrowExceptionOnError(pattern, + "Unknown flag specified for regular expression"); + UNREACHABLE(); + return JSRegExp::null(); + } + } + + // Compile the regex by calling into the jscre library. + uint32_t num_bracket_expressions = 0; + const char* error_msg = NULL; + jscre::JSRegExp* jscregexp = jscre::jsRegExpCompile(two_byte_pattern, + pattern.Length(), + ignore_case, + multi_line, + &num_bracket_expressions, + &error_msg, + &JSREMalloc, + &JSREFree); + + if (jscregexp == NULL) { + // There was an error compiling the regex, Throw an exception. + ThrowExceptionOnError(pattern, error_msg); + UNREACHABLE(); + return JSRegExp::null(); + } else { + // Setup the compiled regex object and return it. + JSRegExp& regexp = + JSRegExp::Handle(JSRegExp::FromDataStartAddress(jscregexp)); + regexp.set_pattern(pattern); + if (multi_line == jscre::JSRegExpMultiline) { + regexp.set_is_multi_line(); + } + if (ignore_case == jscre::JSRegExpIgnoreCase) { + regexp.set_is_ignore_case(); + } + if (is_global) { + regexp.set_is_global(); + } + regexp.set_is_complex(); // Always use jscre library. + regexp.set_num_bracket_expressions(num_bracket_expressions); + return regexp.raw(); + } +} + + +RawArray* Jscre::Execute(const JSRegExp& regex, + const String& str, + intptr_t start_index) { + // First convert the input str to UTF16 format as the jscre library expects + // strings to be in UTF16 encoding. + uint16_t* two_byte_str = GetTwoByteData(str); + + // Execute a regex match by calling into the jscre library. + jscre::JSRegExp* jscregexp = + reinterpret_cast(regex.GetDataStartAddress()); + ASSERT(jscregexp != NULL); + const Smi& num_bracket_exprs = Smi::Handle(regex.num_bracket_expressions()); + intptr_t num_bracket_expressions = num_bracket_exprs.Value(); + Zone* zone = Isolate::Current()->current_zone(); + // The jscre library rounds the passed in size to a multiple of 3 in order + // to reuse the passed in offsets array as a temporary chunk of working + // storage during matching, so we just pass in a size which is a multiple + // of 3. + const int kJscreMultiple = 3; + int offsets_length = (num_bracket_expressions + 1) * kJscreMultiple; + int* offsets = NULL; + int offsets_array_size = offsets_length * sizeof(offsets[0]); + offsets = reinterpret_cast(zone->Allocate(offsets_array_size)); + int retval = jscre::jsRegExpExecute(jscregexp, + two_byte_str, + str.Length(), + start_index, + offsets, + offsets_length); + + // The KJS JavaScript engine returns null (ie, a failed match) when + // JSRE's internal match limit is exceeded. We duplicate that behavior here. + if (retval == jscre::JSRegExpErrorNoMatch + || retval == jscre::JSRegExpErrorHitLimit) { + return Array::null(); + } + + // Other JSRE errors: + if (retval < 0) { + const String& pattern = String::Handle(regex.pattern()); + const int kErrorLength = 256; + char error_msg[kErrorLength]; + OS::SNPrint(error_msg, kErrorLength, + "jscre::jsRegExpExecute error : %d", retval); + ThrowExceptionOnError(pattern, error_msg); + UNREACHABLE(); + return Array::null(); + } + + const int kMatchPair = 2; + Array& array = + Array::Handle(Array::New(kMatchPair * (num_bracket_expressions + 1))); + // The matches come in (start, end + 1) pairs for each bracketted expression. + Smi& start = Smi::Handle(); + Smi& end = Smi::Handle(); + for (intptr_t i = 0; + i < (kMatchPair * (num_bracket_expressions + 1)); + i += kMatchPair) { + start = Smi::New(offsets[i]); + end = Smi::New(offsets[i + 1]); + array.SetAt(i, start); + array.SetAt(i+1, end); + } + return array.raw(); +} + +} // namespace dart diff --git a/runtime/lib/regexp_jsc.h b/runtime/lib/regexp_jsc.h new file mode 100644 index 00000000000..ff600331bf0 --- /dev/null +++ b/runtime/lib/regexp_jsc.h @@ -0,0 +1,23 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef LIB_REGEXP_JSC_H_ +#define LIB_REGEXP_JSC_H_ + +#include "vm/object.h" + + +namespace dart { + +class Jscre : public AllStatic { + public: + static RawJSRegExp* Compile(const String& pattern, const String& flags); + static RawArray* Execute(const JSRegExp& regex, + const String& str, + intptr_t index); +}; + +} // namespace dart + +#endif // LIB_REGEXP_JSC_H_ diff --git a/runtime/lib/string.cc b/runtime/lib/string.cc new file mode 100644 index 00000000000..f6941d5e896 --- /dev/null +++ b/runtime/lib/string.cc @@ -0,0 +1,150 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bootstrap_natives.h" + +#include "vm/exceptions.h" +#include "vm/native_entry.h" +#include "vm/object.h" + +namespace dart { + +DEFINE_NATIVE_ENTRY(StringBase_createFromCodePoints, 1) { + const Array& a = Array::CheckedHandle(arguments->At(0)); + // TODO(srdjan): Check that parameterized type is an int. + Zone* zone = Isolate::Current()->current_zone(); + intptr_t len = a.Length(); + + // Unbox the array and determine the maximum element width. + bool is_one_byte_string = true; + bool is_two_byte_string = true; + uint32_t* temp = reinterpret_cast( + zone->Allocate(len * sizeof(uint32_t))); // NOLINT + Smi& element = Smi::Handle(); + for (intptr_t i = 0; i < len; i++) { + const Object& index_object = Object::Handle(a.At(i)); + if (!index_object.IsSmi()) { + GrowableArray args; + args.Add(&index_object); + Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); + } + element ^= index_object.raw(); + intptr_t value = element.Value(); + if (value < 0) { + GrowableArray args; + Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); + } else if (value > 0xFFFF) { + is_one_byte_string = false; + is_two_byte_string = false; + } else if (value > 0xFF) { + is_one_byte_string = false; + } + temp[i] = value; + } + String& result = String::Handle(); + if (is_one_byte_string) { + result ^= OneByteString::New(temp, len, Heap::kNew); + } else if (is_two_byte_string) { + result ^= TwoByteString::New(temp, len, Heap::kNew); + } else { + result ^= FourByteString::New(temp, len, Heap::kNew); + } + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(String_hashCode, 1) { + const String& str = String::CheckedHandle(arguments->At(0)); + intptr_t hash_val = 0; + if (!str.IsNull()) { + hash_val = str.Hash(); + } + ASSERT(Smi::IsValid(hash_val)); + ASSERT(hash_val > 0); + const Smi& hash_smi = Smi::Handle(Smi::New(hash_val)); + arguments->SetReturn(hash_smi); +} + + +DEFINE_NATIVE_ENTRY(String_getLength, 1) { + const String& str = String::CheckedHandle(arguments->At(0)); + arguments->SetReturn(Smi::Handle(Smi::New(str.Length()))); +} + + +static int32_t StringValueAt(const String& str, const Integer& index) { + if (index.IsSmi()) { + Smi& smi = Smi::Handle(); + smi ^= index.raw(); + int32_t index = smi.Value(); + if ((index < 0) || (index >= str.Length())) { + GrowableArray arguments; + arguments.Add(&smi); + Exceptions::ThrowByType(Exceptions::kIndexOutOfRange, arguments); + } + return str.CharAt(index); + } else { + // TODO(srdjan): Bigint index not supported. + UNIMPLEMENTED(); + return 0; + } +} + + +DEFINE_NATIVE_ENTRY(String_charAt, 2) { + const String& str = String::CheckedHandle(arguments->At(0)); + const Instance& index_instance = Instance::CheckedHandle(arguments->At(1)); + if (!index_instance.IsInteger()) { + GrowableArray args; + args.Add(&index_instance); + Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); + } + Integer& index = Integer::Handle(); + index ^= index_instance.raw(); + uint32_t value = StringValueAt(str, index); + ASSERT(value <= 0x10FFFF); + arguments->SetReturn(String::Handle(String::NewSymbol(&value, 1))); +} + + +DEFINE_NATIVE_ENTRY(String_charCodeAt, 2) { + const String& str = String::CheckedHandle(arguments->At(0)); + const Integer& index_instance = Integer::CheckedHandle(arguments->At(1)); + if (!index_instance.IsInteger()) { + GrowableArray args; + args.Add(&index_instance); + Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); + } + Integer& index = Integer::Handle(); + index ^= index_instance.raw(); + int32_t value = StringValueAt(str, index); + ASSERT(value >= 0); + ASSERT(value <= 0x10FFFF); + arguments->SetReturn(Smi::Handle(Smi::New(value))); +} + + +DEFINE_NATIVE_ENTRY(String_concat, 2) { + const String& a = String::CheckedHandle(arguments->At(0)); + ASSERT(!a.IsNull()); // The receiver cannot be null. + const Instance& b_instance = Instance::CheckedHandle(arguments->At(1)); + if (!b_instance.IsString()) { + GrowableArray args; + Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); + } + String& b = String::Handle(); + b ^= b_instance.raw(); + const String& result = String::Handle(String::Concat(a, b)); + arguments->SetReturn(result); +} + + +DEFINE_NATIVE_ENTRY(Strings_concatAll, 1) { + const Array& strings = Array::CheckedHandle(arguments->At(0)); + ASSERT(!strings.IsNull()); + const String& result = String::Handle(String::ConcatAll(strings)); + arguments->SetReturn(result); +} + +} // namespace dart diff --git a/runtime/lib/string.dart b/runtime/lib/string.dart new file mode 100644 index 00000000000..4361dfce905 --- /dev/null +++ b/runtime/lib/string.dart @@ -0,0 +1,483 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/** + * [StringBase] contains common methods used by concrete String implementations, + * e.g., OneByteString. + */ +class StringBase { + + int hashCode() native "String_hashCode"; + + /** + * Create the most efficient string representation for specified + * [codePoints]. + */ + static String createFromCharCodes(List charCodes) { + ObjectArray objectArray; + if (charCodes is ObjectArray) { + objectArray = charCodes; + } else { + int len = charCodes.length; + objectArray = new Array(len); + for (int i = 0; i < len; i++) { + objectArray[i] = charCodes[i]; + } + } + return _createFromCodePoints(objectArray); + } + + static String _createFromCodePoints(ObjectArray codePoints) + native "StringBase_createFromCodePoints"; + + String operator [](int index) native "String_charAt"; + + int charCodeAt(int index) native "String_charCodeAt"; + + int get length() native "String_getLength"; + + bool isEmpty() { + return this.length === 0; + } + + String concat(String other) native "String_concat"; + + String toString() { + return this; + } + + bool operator ==(Object other) { + if (this === other) { + return true; + } + if (!(other is String) || + (this.length != other.length)) { + // TODO(5413632): Compare hash codes when both are present. + return false; + } + return this.compareTo(other) === 0; + } + + int compareTo(String other) { + int thisLength = this.length; + int otherLength = other.length; + int len = (thisLength < otherLength) ? thisLength : otherLength; + for (int i = 0; i < len; i++) { + int thisCodePoint = this.charCodeAt(i); + int otherCodePoint = other.charCodeAt(i); + if (thisCodePoint < otherCodePoint) { + return -1; + } + if (thisCodePoint > otherCodePoint) { + return 1; + } + } + if (thisLength < otherLength) return -1; + if (thisLength > otherLength) return 1; + return 0; + } + + bool substringMatches(int start, String other) { + if (other.isEmpty()) return true; + if ((start < 0) || (start >= this.length)) { + return false; + } + final int len = other.length; + if ((start + len) > this.length) { + return false; + } + for (int i = 0; i < len; i++) { + if (this.charCodeAt(i + start) != other.charCodeAt(i)) { + return false; + } + } + return true; + } + + bool endsWith(String other) { + return this.substringMatches(this.length - other.length, other); + } + + bool startsWith(String other) { + return this.substringMatches(0, other); + } + + int indexOf(String other, int startIndex) { + if (other.isEmpty()) { + return startIndex < this.length ? startIndex : this.length; + } + if ((startIndex < 0) || (startIndex >= this.length)) { + return -1; + } + int len = this.length - other.length + 1; + for (int index = startIndex; index < len; index++) { + if (this.substringMatches(index, other)) { + return index; + } + } + return -1; + } + + int lastIndexOf(String other, int fromIndex) { + if (other.isEmpty()) { + return Math.min(this.length, fromIndex); + } + if (fromIndex >= this.length) { + fromIndex = this.length - 1; + } + for (int index = fromIndex; index >= 0; index--) { + if (this.substringMatches(index, other)) { + return index; + } + } + return -1; + } + + String substring(int startIndex, int endIndex) { + if ((startIndex < 0) || (startIndex > this.length)) { + throw new IndexOutOfRangeException(startIndex); + } + if ((endIndex < 0) || (endIndex > this.length)) { + throw new IndexOutOfRangeException(endIndex); + } + if (startIndex > endIndex) { + throw new IndexOutOfRangeException(startIndex); + } + return substringUnchecked_(startIndex, endIndex); + } + + // TODO(terry): Temporary workaround until substring can support a default + // argument for endIndex (when the VM supports default args). + // This method is a place holder to flag breakage for apps + // that depend on this behavior of substring. + String substringToEnd(int startIndex) { + return this.substring(startIndex, this.length); + } + + String substringUnchecked_(int startIndex, int endIndex) { + int len = endIndex - startIndex; + Array charCodes = new Array(len); + for (int i = 0; i < len; i++) { + charCodes[i] = this.charCodeAt(startIndex + i); + } + return StringBase.createFromCharCodes(charCodes); + } + + String trim() { + int len = this.length; + int first = 0; + for (; first < len; first++) { + if (!_isWhitespace(this.charCodeAt(first))) { + break; + } + } + if (len == first) { + // String contains only whitespaces. + return ""; + } + int last = len - 1; + for (int i = last; last >= first; last--) { + if (!_isWhitespace(this.charCodeAt(last))) { + break; + } + } + return substringUnchecked_(first, last + 1); + } + + bool contains(Pattern other, int startIndex) { + if (other is RegExp) { + throw "Unimplemented String.contains with RegExp"; + } + return indexOf(other, startIndex) >= 0; + } + + String replaceFirst(Pattern from, String to) { + if (from is RegExp) { + throw "Unimplemented String.replace with RegExp"; + } + int pos = this.indexOf(from, 0); + if (pos < 0) { + return this; + } + String s1 = this.substring(0, pos); + String s2 = this.substring(pos + from.length, this.length); + return s1.concat(to.concat(s2)); + } + + String replaceAll(Pattern from_, String to) { + if (from_ is RegExp) { + throw "Unimplemented String.replaceAll with RegExp"; + } + String from = from_; + int fromLength = from.length; + int toLength = to.length; + int thisLength = this.length; + + StringBuffer result = new StringBuffer(""); + // Special case the empty string replacement where [to] is + // inserted in between each character. + if (fromLength === 0) { + result.add(to); + for (int i = 0; i < thisLength; i++) { + result.add(this.substring(i, i + 1)); + result.add(to); + } + return result.toString(); + } + + int index = indexOf(from, 0); + if (index < 0) { + return this; + } + int startIndex = 0; + do { + result.add(this.substring(startIndex, index)); + result.add(to); + startIndex = index + fromLength; + } while ((index = indexOf(from, startIndex)) >= 0); + + // If there are remaining code points, add them to the string + // buffer. + if (startIndex < thisLength) { + result.add(this.substring(startIndex, thisLength)); + } + + return result.toString(); + } + + /** + * Convert argument obj to string and concat it with this string. + * Returns concatenated string. + */ + String operator +(Object obj) { + return this.concat(obj.toString()); + } + + /** + * Convert all objects in [values] to strings and concat them + * into a result string. + */ + static String _interpolate(Array values) { + int numValues = values.length; + Array stringArray = new Array(numValues); + int resultLength = 0; + for (int i = 0; i < numValues; i++) { + String str = values[i].toString(); + resultLength += str.length; + stringArray[i] = str; + } + Array codepoints = new Array(resultLength); + int intArrayIx = 0; + for (int i = 0; i < numValues; i++) { + String str = stringArray[i]; + int strLength = str.length; + for (int k = 0; k < strLength; k++) { + codepoints[intArrayIx++] = str.charCodeAt(k); + } + } + return StringBase.createFromCharCodes(codepoints); + } + + Iterable allMatches(String str) { + GrowableObjectArray result = new GrowableObjectArray(); + if (this.isEmpty()) return result; + int length = this.length; + + int ix = 0; + while (ix < str.length) { + int foundIx = str.indexOf(this, ix); + if (foundIx < 0) break; + result.add(new _StringMatch(foundIx, str, this)); + ix = foundIx + length; + } + return result; + } + + Array split(Pattern pattern) { + if (pattern is RegExp) { + throw "Unimplemented split with RegExp"; + } + GrowableObjectArray result = new GrowableObjectArray(); + if (pattern.isEmpty()) { + for (int i = 0; i < this.length; i++) { + result.add(this.substring(i, i+1)); + } + return result; + } + int ix = 0; + while (ix < this.length) { + int foundIx = this.indexOf(pattern, ix); + if (foundIx < 0) { + // Not found, add remaining. + result.add(this.substring(ix, this.length)); + break; + } + result.add(this.substring(ix, foundIx)); + ix = foundIx + pattern.length; + } + if (ix == this.length) { + result.add(""); + } + return result; + } + + Array splitChars() { + int len = this.length; + final result = new Array(len); + for (int i = 0; i < len; i++) { + result[i] = this[i]; + } + return result; + } + + Array charCodes() { + int len = this.length; + final result = new Array(len); + for (int i = 0; i < len; i++) { + result[i] = this.charCodeAt(i); + } + return result; + } + + String toLowerCase() { + final int aCode = "A".charCodeAt(0); + final int zCode = "Z".charCodeAt(0); + final int delta = aCode - "a".charCodeAt(0); + return _convert(this, aCode, zCode, delta); + } + + String toUpperCase() { + final int aCode = "a".charCodeAt(0); + final int zCode = "z".charCodeAt(0); + final int delta = aCode - "A".charCodeAt(0); + return _convert(this, aCode, zCode, delta); + } + + static String _convert(String str, int startCode, int endCode, int delta) { + final int len = str.length; + int i = 0; + // Check if we can just return the string. + for (; i < len; i++) { + int code = str.charCodeAt(i); + if ((startCode <= code) && (code <= endCode)) break; + } + if (i == len) return str; + + Array charCodes = new Array(len); + for (i = 0; i < len; i++) { + int code = str.charCodeAt(i); + if ((startCode <= code) && (code <= endCode)) { + code = code - delta; + } + charCodes[i] = code; + } + return StringBase.createFromCharCodes(charCodes); + } + + + + // Implementations of Strings methods follow below. + static String join(Array strings, String separator) { + final int length = strings.length; + if (length === 0) { + return ""; + } + + Array strings_array = strings; + if (separator.length != 0) { + strings_array = new Array(2 * length - 1); + strings_array[0] = strings[0]; + int j = 1; + for (int i = 1; i < length; i++) { + strings_array[j++] = separator; + strings_array[j++] = strings[i]; + } + } + return concatAll(strings_array); + } + + static String concatAll(Array strings) { + ObjectArray strings_array; + if (strings is ObjectArray) { + strings_array = strings; + } else { + int len = strings.length; + strings_array = new Array(len); + for (int i = 0; i < len; i++) { + strings_array[i] = strings[i]; + } + } + return _concatAll(strings_array); + } + + static String _concatAll(ObjectArray strings) + native "Strings_concatAll"; +} + + +class OneByteString extends StringBase implements String { + // Checks for one-byte whitespaces only. + // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid + // whitespaces for one byte strings. + bool _isWhitespace(int codePoint) { + return + (codePoint === 32) || // Space. + ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. + } + +} + + +class TwoByteString extends StringBase implements String { + // Checks for one-byte whitespaces only. + // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid + // whitespaces. Add checking for multi-byte whitespace codepoints. + bool _isWhitespace(int codePoint) { + return + (codePoint === 32) || // Space. + ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. + } +} + + +class FourByteString extends StringBase implements String { + // Checks for one-byte whitespaces only. + // TODO(srdjan): Investigate if 0x85 (NEL) and 0xA0 (NBSP) are valid + // whitespaces. Add checking for multi-byte whitespace codepoints. + bool _isWhitespace(int codePoint) { + return + (codePoint === 32) || // Space. + ((9 <= codePoint) && (codePoint <= 13)); // CR, LF, TAB, etc. + } +} + +class _StringMatch implements Match { + const _StringMatch(int this._start, + String this.str, + String this.pattern); + + int start() => _start; + int end() => _start + pattern.length; + String operator[](int g) => group(g); + int groupCount() => 0; + + String group(int group) { + if (group != 0) { + throw new IndexOutOfRangeException(group); + } + return pattern; + } + + Array groups(Array groups) { + Array result = new Array(); + for (int g in groups) { + result.add(group(g)); + } + return result; + } + + final int _start; + final String str; + final String pattern; +} diff --git a/runtime/lib/string_buffer.dart b/runtime/lib/string_buffer.dart new file mode 100644 index 00000000000..046d5986d8f --- /dev/null +++ b/runtime/lib/string_buffer.dart @@ -0,0 +1,84 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/** + * The StringBuffer class is useful for concatenating strings + * efficiently. Only on a call to [toString] are the strings + * concatenated to a single String. + */ +class StringBufferImpl implements StringBuffer { + /** + * Creates the string buffer with an initial content. + */ + StringBufferImpl([Object content = ""]) { + clear(); + add(content); + } + + /** + * Returns the length of the buffer. + */ + int get length() { + return _length; + } + + bool isEmpty() { + return _length === 0; + } + + /** + * Adds [obj] to the buffer. Returns [this]. + */ + StringBuffer add(Object obj) { + String str = obj.toString(); + if (str === null || str.isEmpty()) return; + _buffer.add(str); + _length += str.length; + return this; + } + + /** + * Adds all items in [objects] to the buffer. Returns [this]. + */ + StringBuffer addAll(Collection objects) { + for (Object obj in objects) { + add(obj); + } + return this; + } + + /** + * Adds the string representation of [charCode] to the buffer. + * Returns [this]. + */ + StringBuffer addCharCode(int charCode) { + return add(new String.fromCharCodes([charCode])); + } + + /** + * Clears the string buffer. Returns [this]. + */ + StringBuffer clear() { + _buffer = new GrowableObjectArray(); + _length = 0; + return this; + } + + /** + * Returns the contents of buffer as a concatenated string. + */ + String toString() { + if (_buffer.length === 0) return ""; + if (_buffer.length === 1) return _buffer[0]; + String result = StringBase.concatAll(_buffer); + _buffer.clear(); + _buffer.add(result); + // Since we track the length at each add operation, there is no + // need to update it in this function. + return result; + } + + GrowableObjectArray _buffer; + int _length; +} diff --git a/runtime/tests/dart/dart.status b/runtime/tests/dart/dart.status new file mode 100644 index 00000000000..9436f64cd5c --- /dev/null +++ b/runtime/tests/dart/dart.status @@ -0,0 +1,42 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +prefix dart + +[ $arch == ia32 || $arch == dartium ] +# Process tests which are pending process changes. +Process*: Skip +ManyEchoServerTest: Skip # Bug 5103754 + + +[ $arch == dartium ] +# Server specific tests OK to fail in the browser +EchoServerTest: Skip # Uses Socket +EchoServerStreamTest: Skip # Uses Socket +EchoServerStreamReadUntilTest: Skip # Uses Socket +SocketCloseTest: Skip # Uses Socket +SocketExceptionTest: Skip # Uses Socket +FileTest: Skip +ManyEchoServerTest: Skip # Uses Socket +TimerTest: Skip # Uses EventHandler +TimerRepeatTest: Skip # Uses EventHandler +MultipleTimerTest: Skip # Uses EventHandler +TimerCancelTest: Skip # Uses EventHandler +FileInputStreamTest: Skip # Uses TextFileInputStream. + + +[ $arch == dartc || $arch == chromium ] +*: Skip + + +[ $arch == x64 ] +*: Skip + + +[ $arch == arm ] +*: Skip + + +[ $arch == simarm ] +*: Skip diff --git a/runtime/tests/dart/src/BigIntegerTest.dart b/runtime/tests/dart/src/BigIntegerTest.dart new file mode 100644 index 00000000000..171e535de0d --- /dev/null +++ b/runtime/tests/dart/src/BigIntegerTest.dart @@ -0,0 +1,174 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Testing Bigints. +// TODO(srdjan): Make sure the numbers are Bigint and not Mint or Smi. +// VMOptions=--expose_core_impl + +class BigIntegerTest { + + static void checkBigint(int a) { + Expect.equals(true, (a is Bigint)); + } + + static foo() { + return 1234567890123456789; + } + + static testSmiOverflow() { + var a = 1073741823; + var b = 1073741822; + Expect.equals(2147483645, a + b); + a = -1000000000; + b = 1000000001; + Expect.equals(-2000000001, a - b); + Expect.equals(-1000000001000000000, a * b); + } + + static testBigintAdd() { + // Bigint and Smi. + var a = 12345678901234567890; + var b = 2; + Expect.equals(12345678901234567892, a + b); + Expect.equals(12345678901234567892, b + a); + // Bigint and Bigint. + a = 10000000000000000001; + Expect.equals(20000000000000000002, a + a); + // Bigint and double. + a = 100000000000.0; + b = 100000000000; + Expect.equals(200000000000.0, a + b); + Expect.equals(200000000000.0, b + a); + } + + static testBigintSub() { + // Bigint and Smi. + var a = 12345678901234567890; + var b = 2; + Expect.equals(12345678901234567888, a - b); + Expect.equals(-12345678901234567888, b - a); + // Bigint and Bigint. + a = 10000000000000000001; + Expect.equals(20000000000000000002, a + a); + // Bigint and double. + a = 100000000000.0; + b = 100000000000; + Expect.equals(200000000000.0, a + b); + Expect.equals(200000000000.0, b + a); + Expect.equals(-1, 0xF00000000 - 0xF00000001); + } + + static testBigintMul() { + // Bigint and Smi. + var a = 12345678901234567890; + var b = 10; + Expect.equals(123456789012345678900, a * b); + Expect.equals(123456789012345678900, b * a); + // Bigint and Bigint. + a = 12345678901234567890; + b = 10000000000000000; + Expect.equals(123456789012345678900000000000000000, a * b); + // Bigint and double. + a = 200.0; + b = 100000000000; + Expect.equals(20000000000000.0, a * b); + Expect.equals(20000000000000.0, b * a); + } + + static testBigintTruncDiv() { + var a = 12345678901234567890; + var b = 10; + // Bigint and Smi. + Expect.equals(1234567890123456789, a ~/ b); + Expect.equals(0, b ~/ a); + Expect.equals(123456789, 123456789012345678 ~/ 1000000000); + // Bigint and Bigint. + a = 12345678901234567890; + b = 10000000000000000; + Expect.equals(1234, a ~/ b); + // Bigint and double. + a = 200.0; + b = 100000000000; + Expect.equals(0.0, a ~/ b); + Expect.equals(500000000.0, b ~/ a); + } + + static testBigintDiv() { + // Bigint and Smi. + Expect.equals(1234567890123456789.1, 12345678901234567891 / 10); + Expect.equals(0.000000001234, 1234 / 1000000000000); + Expect.equals(12345678901234000000.0, 123456789012340000000 / 10); + // Bigint and Bigint. + var a = 12345670000000000000; + var b = 10000000000000000; + Expect.equals(1234.567, a / b); + // Bigint and double. + a = 200.0; + b = 100000000000; + Expect.equals(0.000000002, a / b); + Expect.equals(500000000.0, b / a); + } + + static testBigintRemainder() { + // Bigint and Smi. + var a = 1000000000005; + var b = 10; + Expect.equals(5, a % b); + Expect.equals(10, b % a); + // Bigint & Bigint + a = 10000000000000000001; + b = 10000000000000000000; + Expect.equals(1, a % b); + Expect.equals(10000000000000000000, b % a); + // Bigint & double. + a = 100000000001.0; + b = 100000000000; + Expect.equals(1.0, a % b); + Expect.equals(100000000000.0, b % a); + } + + static testBigintNegate() { + var a = 0xF000000000F; + var b = ~a; // negate. + Expect.equals(-0xF0000000010, b); + Expect.equals(0, a & b); + Expect.equals(-1, a | b); + } + + static testShiftAmount() { + Expect.equals(0, 12 >> 111111111111111111111111111111); + Expect.equals(-1, -12 >> 111111111111111111111111111111); + bool exceptionCaught = false; + try { + var a = 1 << 1111111111111111111111111111; + } catch (OutOfMemoryException e) { + exceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + } + + static testMain() { + Expect.equals(1234567890123456789, foo()); + testSmiOverflow(); + testBigintAdd(); + testBigintSub(); + testBigintMul(); + testBigintRemainder(); + testBigintTruncDiv(); + testBigintDiv(); + testBigintNegate(); + testShiftAmount(); + Expect.equals(1234567890123456, (1234567890123456).abs()); + Expect.equals(1234567890123456, (-1234567890123456).abs()); + var a = 10000000000000000000; + var b = 10000000000000000001; + checkBigint(a); + checkBigint(b); + Expect.equals(false, a.hashCode() == b.hashCode()); + Expect.equals(true, a.hashCode() == (b - 1).hashCode()); + } +} + +main() { + BigIntegerTest.testMain(); +} diff --git a/runtime/tests/dart/src/EchoServerStreamReadUntilTest.dart b/runtime/tests/dart/src/EchoServerStreamReadUntilTest.dart new file mode 100644 index 00000000000..b4aa4ead98a --- /dev/null +++ b/runtime/tests/dart/src/EchoServerStreamReadUntilTest.dart @@ -0,0 +1,185 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Echo server test program to test socket stream read until functionality. + +main() { + EchoServerStreamReadUntilTest.testMain(); +} + +class EchoServerStreamReadUntilTest { + + static void testMain() { + EchoServerGame echoServerGame = new EchoServerGame.start(); + } +} + +class EchoServerGame { + + static final MSGSIZE = 10; + static final SERVERINIT = 0; + static final SERVERSHUTDOWN = -1; + static final MESSAGES = 200; + // Char "A". + static final FIRSTCHAR = 65; + static final List PATTERN = + const [FIRSTCHAR + MSGSIZE - 2, FIRSTCHAR + MSGSIZE - 1]; + + EchoServerGame.start() + : _receivePort = new ReceivePort(), + _sendPort = null, + _buffer = new List(MSGSIZE), + _messages = 0 { + for (int i = 0; i < MSGSIZE; i++) { + _buffer[i] = FIRSTCHAR + i; + } + new EchoServer().spawn().then((SendPort port) { + _sendPort = port; + start(); + }); + } + + void sendData() { + + void closeHandler() { + _socket.close(); + } + + void errorHandler() { + Logger.println("Socket error"); + _socket.close(); + } + + void connectHandler() { + + SocketOutputStream stream = _socket.outputStream; + + void dataSent() { + SocketInputStream stream = _socket.inputStream; + + void dataReceived(List buffer) { + for (int i = 0; i < MSGSIZE; i++) { + Expect.equals(FIRSTCHAR + i, _buffer[i]); + } + _messages++; + _socket.close(); + if (_messages < MESSAGES) { + sendData(); + } else { + shutdown(); + } + } + + stream.readUntil(PATTERN, dataReceived); + } + + _socket.setCloseHandler(closeHandler); + _socket.setErrorHandler(errorHandler); + bool written = stream.write(_buffer, 0, MSGSIZE, dataSent); + if (written) { + dataSent(); + } + } + + _socket = new Socket(EchoServer.HOST, _port); + if (_socket !== null) { + _socket.setConnectHandler(connectHandler); + } + } + + void start() { + _receivePort.receive((var message, SendPort replyTo) { + _port = message; + sendData(); + }); + _sendPort.send(SERVERINIT, _receivePort.toSendPort()); + } + + void shutdown() { + _sendPort.send(SERVERSHUTDOWN, _receivePort.toSendPort()); + _receivePort.close(); + } + + int _port; + ReceivePort _receivePort; + SendPort _sendPort; + Socket _socket; + List _buffer; + int _messages; +} + +class EchoServer extends Isolate { + + static final HOST = "127.0.0.1"; + static final int MSGSIZE = EchoServerGame.MSGSIZE; + static final int FIRSTCHAR = EchoServerGame.FIRSTCHAR; + static final List PATTERN = EchoServerGame.PATTERN; + + void main() { + + void connectionHandler() { + + void messageHandler() { + + void dataReceived(List buffer) { + + SocketOutputStream outputStream = _client.outputStream; + + void dataWritten() { + _client.close(); + } + + for (int i = 0; i < MSGSIZE; i++) { + Expect.equals(EchoServerGame.FIRSTCHAR + i, buffer[i]); + } + bool written = outputStream.write(buffer, 0, MSGSIZE, dataWritten); + if (written) { + dataWritten(); + } + } + + SocketInputStream inputStream = _client.inputStream; + inputStream.readUntil(PATTERN, dataReceived); + } + + void closeHandler() { + _socket.close(); + } + + void errorHandler() { + Logger.println("Socket error"); + _socket.close(); + } + + _client = _server.accept(); + if (_client !== null) { + _client.setDataHandler(messageHandler); + _client.setCloseHandler(closeHandler); + _client.setErrorHandler(errorHandler); + } + } + + void errorHandlerServer() { + Logger.println("Server socket error"); + _server.close(); + } + + this.port.receive((message, SendPort replyTo) { + if (message == EchoServerGame.SERVERINIT) { + _server = new ServerSocket(HOST, 0, 10); + Expect.equals(true, _server !== null); + int port = _server.port; + _server.setConnectionHandler(connectionHandler); + _server.setErrorHandler(errorHandlerServer); + replyTo.send(port, null); + } else if (message == EchoServerGame.SERVERSHUTDOWN) { + _server.close(); + this.port.close(); + } + }); + } + + ServerSocket _server; + Socket _client; +} diff --git a/runtime/tests/dart/src/EchoServerStreamTest.dart b/runtime/tests/dart/src/EchoServerStreamTest.dart new file mode 100644 index 00000000000..f581876f3bb --- /dev/null +++ b/runtime/tests/dart/src/EchoServerStreamTest.dart @@ -0,0 +1,192 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Echo server test program to test socket streams. + +class EchoServerStreamTest { + + static void testMain() { + EchoServerGame echoServerGame = new EchoServerGame.start(); + } +} + +class EchoServerGame { + + static final MSGSIZE = 10; + static final SERVERINIT = 0; + static final SERVERSHUTDOWN = -1; + static final MESSAGES = 200; + static final FIRSTCHAR = 65; + + EchoServerGame.start() + : _receivePort = new ReceivePort(), + _sendPort = null, + _buffer = new List(MSGSIZE), + _messages = 0 { + for (int i = 0; i < MSGSIZE; i++) { + _buffer[i] = FIRSTCHAR + i; + } + new EchoServer().spawn().then((SendPort port) { + _sendPort = port; + start(); + }); + } + + void sendData() { + + void closeHandler() { + _socket.close(); + } + + void errorHandler() { + print("Socket error"); + _socket.close(); + } + + void connectHandler() { + + SocketOutputStream stream = _socket.outputStream; + + void dataSent() { + // Reset buffer + for (int i = 0; i < MSGSIZE; i++) { + _buffer[i] = 1; + } + SocketInputStream stream = _socket.inputStream; + + void dataReceived() { + for (int i = 0; i < MSGSIZE; i++) { + Expect.equals(FIRSTCHAR + i, _buffer[i]); + } + _messages++; + _socket.close(); + if (_messages < MESSAGES) { + sendData(); + } else { + shutdown(); + } + } + + bool read = stream.read(_buffer, 0, MSGSIZE, dataReceived); + if (read) { + dataReceived(); + } + } + + _socket.setCloseHandler(closeHandler); + _socket.setErrorHandler(errorHandler); + bool written = stream.write(_buffer, 0, MSGSIZE, dataSent); + if (written) { + dataSent(); + } + } + + _socket = new Socket(EchoServer.HOST, _port); + if (_socket !== null) { + _socket.setConnectHandler(connectHandler); + } + } + + void start() { + _receivePort.receive((var message, SendPort replyTo) { + _port = message; + sendData(); + }); + _sendPort.send(SERVERINIT, _receivePort.toSendPort()); + } + + void shutdown() { + _sendPort.send(SERVERSHUTDOWN, _receivePort.toSendPort()); + _receivePort.close(); + } + + int _port; + ReceivePort _receivePort; + SendPort _sendPort; + Socket _socket; + List _buffer; + int _messages; +} + +class EchoServer extends Isolate { + + static final HOST = "127.0.0.1"; + static final int MSGSIZE = EchoServerGame.MSGSIZE; + + // TODO(hpayer): can be removed as soon as we have default constructors + EchoServer() : super() { } + + void main() { + + void connectionHandler() { + + void messageHandler() { + + List buffer = new List(MSGSIZE); + + void dataReceived() { + + SocketOutputStream outputStream = _client.outputStream; + + void dataWritten() { + _client.close(); + } + + for (int i = 0; i < MSGSIZE; i++) { + Expect.equals(EchoServerGame.FIRSTCHAR + i, buffer[i]); + } + bool written = outputStream.write(buffer, 0, MSGSIZE, dataWritten); + if (written) { + dataWritten(); + } + } + + SocketInputStream inputStream = _client.inputStream; + bool read = inputStream.read(buffer, 0, MSGSIZE, dataReceived); + if (read) { + dataReceived(); + } + } + + void closeHandler() { + _socket.close(); + } + + void errorHandler() { + print("Socket error"); + _socket.close(); + } + + _client = _server.accept(); + _client.setDataHandler(messageHandler); + _client.setCloseHandler(closeHandler); + _client.setErrorHandler(errorHandler); + } + + void errorHandlerServer() { + print("Server socket error"); + _server.close(); + } + + this.port.receive((message, SendPort replyTo) { + if (message == EchoServerGame.SERVERINIT) { + _server = new ServerSocket(HOST, 0, 10); + Expect.equals(true, _server !== null); + _server.setConnectionHandler(connectionHandler); + _server.setErrorHandler(errorHandlerServer); + replyTo.send(_server.port, null); + } else if (message == EchoServerGame.SERVERSHUTDOWN) { + _server.close(); + this.port.close(); + } + }); + } + + ServerSocket _server; + Socket _client; +} + +main() { + EchoServerStreamTest.testMain(); +} diff --git a/runtime/tests/dart/src/EchoServerTest.dart b/runtime/tests/dart/src/EchoServerTest.dart new file mode 100644 index 00000000000..01b7124fcd6 --- /dev/null +++ b/runtime/tests/dart/src/EchoServerTest.dart @@ -0,0 +1,239 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Echo server test program for testing sockets. + +class EchoServerTest { + + static void testMain() { + EchoServerGame echoServerGame = new EchoServerGame.start(); + } +} + +class EchoServerGame { + + static final MSGSIZE = 10; + static final SERVERINIT = 0; + static final SERVERSHUTDOWN = -1; + static final MESSAGES = 200; + static final FIRSTCHAR = 65; + + EchoServerGame.start() + : _receivePort = new ReceivePort(), + _sendPort = null, + _buffer = new List(MSGSIZE), + _messages = 0 { + for (int i = 0; i < MSGSIZE; i++) { + _buffer[i] = FIRSTCHAR + i; + } + new EchoServer().spawn().then((SendPort port) { + _sendPort = port; + start(); + }); + } + + void sendData() { + + void messageHandler() { + + List bufferReceived = new List(MSGSIZE); + int bytesRead = 0; + + void handleRead() { + + if (_socket.available() > 0) { + bytesRead += _socket.readList( + bufferReceived, bytesRead, MSGSIZE - bytesRead); + } + if (bytesRead < MSGSIZE) { + /* + * We check every time the whole buffer to verify data integrity. + */ + for (int i = 0; i < bytesRead; i++) { + Expect.equals(FIRSTCHAR + i, bufferReceived[i]); + } + _socket.setDataHandler(handleRead); + } + else { + /* + * We check every time the whole buffer to verify data integrity. + */ + for (int i = 0; i < MSGSIZE; i++) { + Expect.equals(FIRSTCHAR + i, bufferReceived[i]); + } + _messages++; + _socket.close(); + if (_messages < MESSAGES) { + sendData(); + } else { + shutdown(); + } + } + } + + handleRead(); + } + + void closeHandler() { + _socket.close(); + } + + void errorHandler() { + print("Socket error"); + _socket.close(); + } + + void connectHandler() { + + + void writeMessage() { + int bytesWritten = 0; + + void handleWrite() { + bytesWritten += _socket.writeList( + _buffer, bytesWritten, MSGSIZE - bytesWritten); + if (bytesWritten < MSGSIZE) { + _socket.setWriteHandler(handleWrite); + } + } + + handleWrite(); + } + + _socket.setDataHandler(messageHandler); + _socket.setCloseHandler(closeHandler); + _socket.setErrorHandler(errorHandler); + writeMessage(); + } + + _socket = new Socket(EchoServer.HOST, _port); + if (_socket !== null) { + _socket.setConnectHandler(connectHandler); + } + } + + void start() { + _receivePort.receive((var message, SendPort replyTo) { + _port = message; + sendData(); + }); + _sendPort.send(SERVERINIT, _receivePort.toSendPort()); + } + + void shutdown() { + _sendPort.send(SERVERSHUTDOWN, _receivePort.toSendPort()); + _receivePort.close(); + } + + int _port; + ReceivePort _receivePort; + SendPort _sendPort; + Socket _socket; + List _buffer; + int _messages; +} + +class EchoServer extends Isolate { + + static final HOST = "127.0.0.1"; + static final msgSize = EchoServerGame.MSGSIZE; + + + // TODO(hpayer): can be removed as soon as we have default constructors + EchoServer() : super() { } + + void main() { + + void connectionHandler() { + + void messageHandler() { + + List buffer = new List(msgSize); + int bytesRead = 0; + + void handleRead() { + if (_client.available() > 0) { + bytesRead += _client.readList(buffer, bytesRead, msgSize - bytesRead); + } + if (bytesRead < msgSize) { + /* + * We check every time the whole buffer to verify data integrity. + */ + for (int i = 0; i < bytesRead; i++) { + Expect.equals(EchoServerGame.FIRSTCHAR + i, buffer[i]); + } + _client.setDataHandler(handleRead); + } + else { + _client.setDataHandler(null); + /* + * We check every time the whole buffer to verify data integrity. + */ + for (int i = 0; i < msgSize; i++) { + Expect.equals(EchoServerGame.FIRSTCHAR + i, buffer[i]); + } + + void writeMessage() { + + int bytesWritten = 0; + + void handleWrite() { + bytesWritten += _client.writeList( + buffer, bytesWritten, msgSize - bytesWritten); + if (bytesWritten < msgSize) { + _client.setWriteHandler(handleWrite); + } else { + _client.close(); + } + } + handleWrite(); + } + writeMessage(); + } + } + + handleRead(); + } + + void closeHandler() { + _socket.close(); + } + + void errorHandler() { + print("Socket error"); + _socket.close(); + } + + _client = _server.accept(); + _client.setDataHandler(messageHandler); + _client.setCloseHandler(closeHandler); + _client.setErrorHandler(errorHandler); + } + + void errorHandlerServer() { + print("Server socket error"); + _server.close(); + } + + this.port.receive((message, SendPort replyTo) { + if (message == EchoServerGame.SERVERINIT) { + _server = new ServerSocket(HOST, 0, 10); + Expect.equals(true, _server !== null); + _server.setConnectionHandler(connectionHandler); + _server.setErrorHandler(errorHandlerServer); + replyTo.send(_server.port, null); + } else if (message == EchoServerGame.SERVERSHUTDOWN) { + _server.close(); + this.port.close(); + } + }); + } + + ServerSocket _server; + Socket _client; +} + +main() { + EchoServerTest.testMain(); +} diff --git a/runtime/tests/dart/src/FileInputStreamTest.dart b/runtime/tests/dart/src/FileInputStreamTest.dart new file mode 100644 index 00000000000..a710fcf168a --- /dev/null +++ b/runtime/tests/dart/src/FileInputStreamTest.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Testing FileInputStream, VM-only, standalone test. + + +String callbackString = null; + +callback(List buffer) { + callbackString = new String.fromCharCodes(buffer); +} + +// Helper method to be able to run the test from the runtime +// directory, or the top directory. +String getFilename(String path) => + FileUtil.fileExists(path) ? path : 'runtime/' + path; + +main() { + String fName = getFilename("tests/dart/src/readuntil_test.dat"); + // File contains "Hello Dart, wassup!" + File file = new File(fName, false); + FileInputStream x = new FileInputStream(file); + x.readUntil("Dart".charCodes(), callback); + file.close(); + Expect.stringEquals("Hello Dart", callbackString); + + callbackString = null; + file = new File(fName, false); + x = new FileInputStream(file); + x.readUntil("Darty".charCodes(), callback); + file.close(); + Expect.isNull(callbackString); + + file = new File(fName, false); + x = new FileInputStream(file); + x.readUntil("wassup!".charCodes(), callback); + file.close(); + Expect.stringEquals("Hello Dart, wassup!", callbackString); +} diff --git a/runtime/tests/dart/src/FileTest.dart b/runtime/tests/dart/src/FileTest.dart new file mode 100644 index 00000000000..855f215ba1f --- /dev/null +++ b/runtime/tests/dart/src/FileTest.dart @@ -0,0 +1,394 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Dart test program for testing file I/O. + + +class FileTest { + // Test for file read functionality. + static int testReadStream() { + // Read a file and check part of it's contents. + String filename = getFilename("bin/file_test.cc"); + File file = new File(filename, false); + InputStream input = file.inputStream; + List buffer = new List(42); + bool readDone = input.read(buffer, 0, 12, null); + Expect.equals(true, readDone); + readDone = input.read(buffer, 12, 30, null); + Expect.equals(true, readDone); + Expect.equals(47, buffer[0]); // represents '/' in the file. + Expect.equals(47, buffer[1]); // represents '/' in the file. + Expect.equals(32, buffer[2]); // represents ' ' in the file. + Expect.equals(67, buffer[3]); // represents 'C' in the file. + Expect.equals(111, buffer[4]); // represents 'o' in the file. + Expect.equals(112, buffer[5]); // represents 'p' in the file. + Expect.equals(121, buffer[6]); // represents 'y' in the file. + Expect.equals(114, buffer[7]); // represents 'r' in the file. + Expect.equals(105, buffer[8]); // represents 'i' in the file. + Expect.equals(103, buffer[9]); // represents 'g' in the file. + Expect.equals(104, buffer[10]); // represents 'h' in the file. + Expect.equals(116, buffer[11]); // represents 't' in the file. + return 1; + } + // Test for file read and write functionality. + static int testReadWriteStream() { + // Read a file. + String inFilename = getFilename("tests/vm/data/fixed_length_file"); + File file = new File(inFilename, false); + InputStream input = file.inputStream; + List buffer1 = new List(42); + bool readDone = input.read(buffer1, 0, 42, null); + Expect.equals(true, readDone); + file.close(); + // Write the contents of the file just read into another file. + String outFilename = getFilename("tests/vm/data/fixed_length_file_out"); + file = new File(outFilename, true); + OutputStream output = file.outputStream; + bool writeDone = output.write(buffer1, 0, 42, null); + Expect.equals(true, writeDone); + file.close(); + // Now read the contents of the file just written. + List buffer2 = new List(42); + file = new File(outFilename, false); + input = file.inputStream; + readDone = input.read(buffer2, 0, 42, null); + Expect.equals(true, readDone); + file.close(); + // Now compare the two buffers to check if they are identical. + for (int i = 0; i < buffer1.length; i++) { + Expect.equals(buffer1[i], buffer2[i]); + } + return 1; + } + static int testRead() { + // Read a file and check part of it's contents. + String filename = getFilename("bin/file_test.cc"); + File file = new File(filename, false); + assert(file != null); + List buffer = new List(42); + int bytes_read = 0; + bytes_read = file.readList(buffer, 0, 12); + Expect.equals(12, bytes_read); + bytes_read = file.readList(buffer, 12, 30); + Expect.equals(30, bytes_read); + Expect.equals(47, buffer[0]); // represents '/' in the file. + Expect.equals(47, buffer[1]); // represents '/' in the file. + Expect.equals(32, buffer[2]); // represents ' ' in the file. + Expect.equals(67, buffer[3]); // represents 'C' in the file. + Expect.equals(111, buffer[4]); // represents 'o' in the file. + Expect.equals(112, buffer[5]); // represents 'p' in the file. + Expect.equals(121, buffer[6]); // represents 'y' in the file. + Expect.equals(114, buffer[7]); // represents 'r' in the file. + Expect.equals(105, buffer[8]); // represents 'i' in the file. + Expect.equals(103, buffer[9]); // represents 'g' in the file. + Expect.equals(104, buffer[10]); // represents 'h' in the file. + Expect.equals(116, buffer[11]); // represents 't' in the file. + return 1; + } + // Test for file read and write functionality. + static int testReadWrite() { + // Read a file. + String inFilename = getFilename("tests/vm/data/fixed_length_file"); + File file = new File(inFilename, false); + List buffer1 = new List(42); + int bytes_read = 0; + int bytes_written = 0; + bytes_read = file.readList(buffer1, 0, 42); + Expect.equals(42, bytes_read); + file.close(); + // Write the contents of the file just read into another file. + String outFilenameBase = "tests/vm/data/fixed_length_file_out"; + file = createFile(outFilenameBase); + Expect.isNotNull(file); + file.writeList(buffer1, 0, bytes_read); + file.close(); + // Now read the contents of the file just written. + List buffer2 = new List(bytes_read); + file = new File(getFilename(outFilenameBase), false); + assert(file != null); + bytes_read = file.readList(buffer2, 0, 42); + Expect.equals(42, bytes_read); + file.close(); + // Now compare the two buffers to check if they are identical. + Expect.equals(buffer1.length, buffer2.length); + for (int i = 0; i < buffer1.length; i++) { + Expect.equals(buffer1[i], buffer2[i]); + } + return 1; + } + + // Test for file length functionality. + static int testLength() { + String filename = getFilename("tests/vm/data/fixed_length_file"); + File input = new File(filename, false); + assert(input != null); + Expect.equals(42, input.length); + return 1; + } + // Test for file position functionality. + static int testPosition() { + String filename = getFilename("tests/vm/data/fixed_length_file"); + File input = new File(filename, false); + assert(input != null); + Expect.equals(0, input.position); + List buffer = new List(100); + input.readList(buffer, 0, 12); + Expect.equals(12, input.position); + input.readList(buffer, 12, 6); + Expect.equals(18, input.position); + return 1; + } + // Tests exception handling after file was closed. + static int testCloseException() { + bool exceptionCaught = false; + bool wrongExceptionCaught = false; + String filename = getFilename("tests/vm/data/fixed_length_file_out"); + File input = new File(filename, true); + Expect.isNotNull(input); + assert(input != null); + input.close(); + try { + input.readByte(); + } catch (FileIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + input.writeByte(1); + } catch (FileIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + input.writeString("Test"); + } catch (FileIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(100); + input.readList(buffer, 0, 10); + } catch (FileIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(100); + input.writeList(buffer, 0, 10); + } catch (FileIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + input.position; + } catch (FileIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + input.length; + } catch (FileIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + input.flush(); + } catch (FileIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + return 1; + } + // Tests stream exception handling after file was closed. + static int testCloseExceptionStream() { + bool exceptionCaught = false; + bool wrongExceptionCaught = false; + String filename = getFilename("tests/vm/data/fixed_length_file_out"); + File file = new File(filename, true); + assert(file != null); + file.close(); + InputStream input = file.inputStream; + try { + List buffer = new List(42); + bool readDone = input.read(buffer, 0, 12, null); + } catch (FileIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + OutputStream output = file.outputStream; + try { + List buffer = new List(42); + bool readDone = output.write(buffer, 0, 12, null); + } catch (FileIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + return 1; + } + // Tests buffer out of bounds exception. + static int testBufferOutOfBoundsException() { + bool exceptionCaught = false; + bool wrongExceptionCaught = false; + String filename = getFilename("tests/vm/data/fixed_length_file_out"); + File file = new File(filename, true); + assert(file != null); + try { + List buffer = new List(10); + bool readDone = file.readList(buffer, 0, 12); + } catch (IndexOutOfRangeException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(10); + bool readDone = file.readList(buffer, 6, 6); + } catch (IndexOutOfRangeException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(10); + bool readDone = file.readList(buffer, -1, 1); + } catch (IndexOutOfRangeException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(10); + bool readDone = file.readList(buffer, 0, -1); + } catch (IndexOutOfRangeException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(10); + bool readDone = file.writeList(buffer, 0, 12); + } catch (IndexOutOfRangeException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(10); + bool readDone = file.writeList(buffer, 6, 6); + } catch (IndexOutOfRangeException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(10); + bool readDone = file.writeList(buffer, -1, 1); + } catch (IndexOutOfRangeException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(10); + bool readDone = file.writeList(buffer, 0, -1); + } catch (IndexOutOfRangeException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + + return 1; + } + + // Helper method to be able to run the test from the runtime + // directory, or the top directory. + static String getFilename(String path) => + FileUtil.fileExists(path) ? path : 'runtime/' + path; + + static File createFile(String path) { + File file = new File(path, true); + if (file === null) { + file = new File('runtime/' + path, true); + } + return file; + } + + // Main test entrypoint. + static testMain() { + Expect.equals(1, testRead()); + Expect.equals(1, testReadWrite()); + Expect.equals(1, testReadStream()); + Expect.equals(1, testReadWriteStream()); + Expect.equals(1, testLength()); + Expect.equals(1, testPosition()); + Expect.equals(1, testCloseException()); + Expect.equals(1, testCloseExceptionStream()); + Expect.equals(1, testBufferOutOfBoundsException()); + } +} + +main() { + FileTest.testMain(); +} diff --git a/runtime/tests/dart/src/GrowableObjectArray2Test.dart b/runtime/tests/dart/src/GrowableObjectArray2Test.dart new file mode 100644 index 00000000000..7f9d5d20e23 --- /dev/null +++ b/runtime/tests/dart/src/GrowableObjectArray2Test.dart @@ -0,0 +1,72 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Test GrowableArray.dart. +// VMOptions=--expose_core_impl + +class GrowableObjectArray2Test { + static testMain() { + var g = new GrowableObjectArray(); + Expect.equals(true, g is Array); + Expect.equals(true, g is GrowableObjectArray); + Expect.equals(true, g.isEmpty()); + for (int i = 0; i < 100; i++) { + g.add(1); + } + g.add(1001); + Expect.equals(101, g.length); + Expect.equals(1001, g[100]); + Expect.equals(false, g.isEmpty()); + Expect.equals(1001, g.last()); + Expect.equals(1001, g.removeLast()); + Expect.equals(100, g.length); + + var f = new GrowableObjectArray(); + var object_array = new Array(20); + for (int i = 0; i < object_array.length; i++) { + f.add(2); + object_array[i] = 5; + } + object_array.copyFrom(f, 0, 0, f.length); + for (int i = 0; i < f.length; i++) { + Expect.equals(2, object_array[i]); + } + f.copyFrom(g, 10, 0, 2); + Expect.equals(20, f.length); + + bool exception_caught = false; + try { + var elem = g[g.length]; + } catch (IndexOutOfRangeException e) { + exception_caught = true; + } + Expect.equals(true, exception_caught); + + Array plain_array = [4, 3, 9, 12, -4, 9]; + GrowableObjectArray h = new GrowableObjectArray.withCapacity(4); + plain_array.forEach((elem) { h.add(elem); }); + int compare(a, b) { + if (a < b) return -1; + if (a > b) return 1; + return 0; + } + h.sort(compare); + Expect.equals(6, h.length); + Expect.equals(-4, h[0]); + Expect.equals(12, h[h.length - 1]); + Set t = new Set.from(h); + Expect.equals(true, t.contains(9)); + Expect.equals(true, t.contains(-4)); + Expect.equals(false, t.contains(-3)); + Expect.equals(5, t.length); + + h.clear(); + Array array = const [0, 1, 2, 3, 4]; + h.addAll(array); + Expect.equals(5, h.length); + } +} + +main() { + GrowableObjectArray2Test.testMain(); +} diff --git a/runtime/tests/dart/src/GrowableObjectArrayTest.dart b/runtime/tests/dart/src/GrowableObjectArrayTest.dart new file mode 100644 index 00000000000..09f8de0de50 --- /dev/null +++ b/runtime/tests/dart/src/GrowableObjectArrayTest.dart @@ -0,0 +1,90 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// VMOptions=--expose_core_impl + +class GrowableObjectArrayTest { + + static void testOutOfBoundForIndexOf() { + GrowableObjectArray array = new GrowableObjectArray(); + for (int i = 0; i < 64; i++) { + array.add(i); + Expect.equals(-1, array.indexOf(4, i + 1)); + Expect.equals(-1, array.lastIndexOf(4, -1)); + } + } + + static testMain() { + GrowableObjectArray array = new GrowableObjectArray(); + array.add(1); + array.add(2); + array.add(3); + array.add(4); + array.add(1); + + Expect.equals(3, array.indexOf(4, 0)); + Expect.equals(0, array.indexOf(1, 0)); + Expect.equals(4, array.lastIndexOf(1, array.length - 1)); + + Expect.equals(4, array.indexOf(1, 1)); + Expect.equals(-1, array.lastIndexOf(4, 2)); + + Expect.equals(5, array.length); + bool found = false; + array = array.filter(bool _(e) { + return found || !(found = (e == 1)); + }); + + Expect.equals(4, array.length); + + Expect.equals(2, array[0]); + Expect.equals(3, array[1]); + Expect.equals(4, array[2]); + Expect.equals(1, array[3]); + + Expect.equals(1, array.removeLast()); + Expect.equals(3, array.length); + Expect.equals(2, array[0]); + Expect.equals(3, array[1]); + Expect.equals(4, array[2]); + + Expect.equals(-1, array.indexOf(6, 0)); + + array.clear(); + array.add(1); + array.add(1); + array.add(1); + array.add(1); + array.add(2); + + Expect.equals(5, array.length); + array = array.filter((e) => e != 1 ); + Expect.equals(1, array.length); + Expect.equals(2, array[0]); + + // Check correct copy order/ + array = new GrowableObjectArray(); + for (int i = 0; i < 10; i++) { + array.add(i); + } + array.copyFrom(array, 7, 8, 2); + Expect.equals(7, array[7]); + Expect.equals(7, array[8]); + Expect.equals(8, array[9]); + array.copyFrom(array, 5, 4, 2); + Expect.equals(5, array[4]); + Expect.equals(6, array[5]); + Expect.equals(6, array[6]); + + testOutOfBoundForIndexOf(); + + GrowableObjectArray h = new GrowableObjectArray.withCapacity(10); + Array constArray = const [0, 1, 2, 3, 4]; + h.addAll(constArray); + Expect.equals(5, h.length); + } +} + +main() { + GrowableObjectArrayTest.testMain(); +} diff --git a/runtime/tests/dart/src/ManyEchoServerTest.dart b/runtime/tests/dart/src/ManyEchoServerTest.dart new file mode 100644 index 00000000000..bac5ded4d31 --- /dev/null +++ b/runtime/tests/dart/src/ManyEchoServerTest.dart @@ -0,0 +1,17 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Stress test isolate generation. +// DartOptions=-- runtime/tests/dart/src/EchoServerTest.dart runtime/tests/dart/src/ManyEchoServerTest.dart -- ManyEchoServerTest.testMain + +class ManyEchoServerTest { + static testMain() { + for (int i = 0; i < 5000; i++) { + EchoServerTest.testMain(); + } + } +} + +main() { + ManyEchoServerTest.testMain(); +} diff --git a/runtime/tests/dart/src/MediumIntegerTest.dart b/runtime/tests/dart/src/MediumIntegerTest.dart new file mode 100644 index 00000000000..5be5ff43d0a --- /dev/null +++ b/runtime/tests/dart/src/MediumIntegerTest.dart @@ -0,0 +1,152 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Testing Mints. Note that the tests may not work on 64-bit machines, +// as Smi's would be used to represent many of the numbers. +// VMOptions=--expose_core_impl + +class MediumIntegerTest { + + static void checkSmi(int a) { + Expect.equals(true, (a is Smi)); + } + + static void checkMint(int a) { + Expect.equals(true, (a is Mint)); + } + + static void checkBigint(int a) { + Expect.equals(true, (a is Bigint)); + } + + static int getMint() { + return 1234567890123456789; + } + + static testSmiOverflow() { + int a = 1073741823; + int b = 1073741822; + checkSmi(a); + checkSmi(b); + checkMint(a + b); + Expect.equals(2147483645, a + b); + checkMint(a * b); + Expect.equals(1152921501385621506, a * b); + checkMint(-a - b); + Expect.equals(-2147483645, -a - b); + } + + static testMintAdd() { + // Mint and Smi. + var a = 1234567890123456789; + var b = 2; + checkMint(a); + checkSmi(b); + checkMint(a + b); + Expect.equals(1234567890123456791, a + b); + Expect.equals(1234567890123456791, b + a); + a = 9223372036854775807; + checkMint(a); + checkBigint(a + 1); + Expect.equals(9223372036854775808, a + 1); + + // Mint and Mint. + a = 100000000000000001; + checkMint(a); + Expect.equals(200000000000000002, a + a); + a = 9223372036854775800; + b = 1000000000000000000; + checkMint(a); + checkMint(b); + checkBigint(a + b); + Expect.equals(10223372036854775800, a + b); + + // Mint and Bigint. + a = 100000000000000001; + b = 10000000000000000001; + checkMint(a); + checkBigint(b); + Expect.equals(10100000000000000002, a + b); + + // Mint and double. + a = 100000000000.0; + b = 100000000000; + checkMint(b); + Expect.equals(200000000000.0, a + b); + Expect.equals(200000000000.0, b + a); + } + + static testMintSub() { + // Mint and Smi. + var a = 1234567890123456789; + var b = 2; + checkMint(a); + checkSmi(b); + checkMint(a - b); + Expect.equals(1234567890123456787, a - b); + a = -9223372036854775808; + checkMint(a); + checkBigint(a - 1); + Expect.equals(-9223372036854775809, a - 1); + + // Mint and Mint. + a = 1234567890123456789; + b = 1000000000000000000; + checkMint(a); + checkMint(b); + checkMint(a - b); + Expect.equals(234567890123456789, a - b); + a = -9223372036854775808; + b = 1000000000000000000; + checkMint(a); + checkMint(b); + checkBigint(a - b); + Expect.equals(-10223372036854775808, a - b); + } + + static testMintDiv() { + // Mint and Smi. + var a = 1234567890123456788; + var b = 2; + checkMint(a); + checkSmi(b); + Expect.equals(617283945061728394.0, a / b); + } + + static testMintMul() { + // Mint and Smi. + var a = 4611686018427387904; + var b = 10; + checkMint(a); + checkSmi(b); + checkBigint(a * b); + Expect.equals(46116860184273879040, a * b); + b = 1000000000000000000; + checkMint(a); + checkMint(b); + checkBigint(a * b); + Expect.equals(4611686018427387904000000000000000000, a * b); + } + + // TODO(srdjan): Add more tests. + + static void testMain() { + checkMint(getMint()); + Expect.equals(1234567890123456789, getMint()); + testSmiOverflow(); + testMintAdd(); + testMintSub(); + testMintMul(); + testMintDiv(); + var a = 100000000000; + var b = 100000000001; + checkMint(a); + checkMint(b); + Expect.equals(false, a.hashCode() == b.hashCode()); + Expect.equals(true, a.hashCode() == (b - 1).hashCode()); + } +} + +main() { + MediumIntegerTest.testMain(); +} diff --git a/runtime/tests/dart/src/MultipleTimerTest.dart b/runtime/tests/dart/src/MultipleTimerTest.dart new file mode 100644 index 00000000000..763eec4d7b8 --- /dev/null +++ b/runtime/tests/dart/src/MultipleTimerTest.dart @@ -0,0 +1,73 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class MultipleTimerTest { + + static final int TIMEOUT1 = 1000; + static final int TIMEOUT2 = 2000; + static final int TIMEOUT3 = 500; + static final int TIMEOUT4 = 1500; + + static void testMultipleTimer() { + + void timeoutHandler1(Timer timer) { + int endTime = (new DateTime.now()).value; + Expect.equals(true, (endTime - _startTime1) >= TIMEOUT1); + Expect.equals(true, _order[_message] == 0); + _message++; + } + + void timeoutHandler2(Timer timer) { + int endTime = (new DateTime.now()).value; + Expect.equals(true, (endTime - _startTime2) >= TIMEOUT2); + Expect.equals(true, _order[_message] == 1); + _message++; + } + + void timeoutHandler3(Timer timer) { + int endTime = (new DateTime.now()).value; + Expect.equals(true, (endTime - _startTime3) >= TIMEOUT3); + Expect.equals(true, _order[_message] == 2); + _message++; + } + + void timeoutHandler4(Timer timer) { + int endTime = (new DateTime.now()).value; + Expect.equals(true, (endTime - _startTime4) >= TIMEOUT4); + Expect.equals(true, _order[_message] == 3); + _message++; + } + + _order = new List(4); + _order[0] = 2; + _order[1] = 0; + _order[2] = 3; + _order[3] = 1; + _message = 0; + + _startTime1 = (new DateTime.now()).value; + new Timer(timeoutHandler1, TIMEOUT1, false); + _startTime2 = (new DateTime.now()).value; + new Timer(timeoutHandler2, TIMEOUT2, false); + _startTime3 = (new DateTime.now()).value; + new Timer(timeoutHandler3, TIMEOUT3, false); + _startTime4 = (new DateTime.now()).value; + new Timer(timeoutHandler4, TIMEOUT4, false); + } + + static void testMain() { + testMultipleTimer(); + } + + static int _startTime1; + static int _startTime2; + static int _startTime3; + static int _startTime4; + static List _order; + static int _message; +} + +main() { + MultipleTimerTest.testMain(); +} diff --git a/runtime/tests/dart/src/ProcessExitTest.dart b/runtime/tests/dart/src/ProcessExitTest.dart new file mode 100644 index 00000000000..91727503f06 --- /dev/null +++ b/runtime/tests/dart/src/ProcessExitTest.dart @@ -0,0 +1,29 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Process test program to test process communication. + +class ProcessExitTest { + + static void testExit() { + Process process = new Process("./process_test", + const ["0", "0", "99", "0"]); + + void exitHandler(int exitCode) { + Expect.equals(exitCode, 99); + process.close(); + } + + process.setExitHandler(exitHandler); + process.start(); + } + + static void testMain() { + testExit(); + } +} + +main() { + ProcessExitTest.testMain(); +} diff --git a/runtime/tests/dart/src/ProcessSegfaultTest.dart b/runtime/tests/dart/src/ProcessSegfaultTest.dart new file mode 100644 index 00000000000..53b6ef422f5 --- /dev/null +++ b/runtime/tests/dart/src/ProcessSegfaultTest.dart @@ -0,0 +1,27 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Process test program to test process communication. + +class ProcessSegfaultTest { + + static void testExit() { + Process process = new Process("./process_test", const ["0", "0", "1", "1"]); + + void exitHandler(int exitCode) { + process.close(); + } + + process.setExitHandler(exitHandler); + process.start(); + } + + static void testMain() { + testExit(); + } +} + +main() { + ProcessSegfaultTest.testMain(); +} diff --git a/runtime/tests/dart/src/ProcessStartExceptionTest.dart b/runtime/tests/dart/src/ProcessStartExceptionTest.dart new file mode 100644 index 00000000000..2063601e742 --- /dev/null +++ b/runtime/tests/dart/src/ProcessStartExceptionTest.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Process test program to test process communication. + +class ProcessStartExceptionTest { + + static void testStartException() { + Process process = + new Process("__path_to_something_that_hopefully_does_not_exist__", + const []); + + void exitHandler(int exitCode) { + exitHandlerCalled = false; + } + + process.setExitHandler(exitHandler); + try { + process.start(); + } catch (ProcessException e) { + errorCode = e.errorCode; + } + } + + static void testMain() { + exitHandlerCalled = false; + testStartException(); + Expect.equals(2, errorCode); + Expect.equals(false, exitHandlerCalled); + } + + static int errorCode; + static bool exitHandlerCalled; +} + +main() { + ProcessStartExceptionTest.testMain(); +} diff --git a/runtime/tests/dart/src/ProcessStderrTest.dart b/runtime/tests/dart/src/ProcessStderrTest.dart new file mode 100644 index 00000000000..929169cd81e --- /dev/null +++ b/runtime/tests/dart/src/ProcessStderrTest.dart @@ -0,0 +1,53 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Process test program to test process communication. + +class ProcessStderrTest { + + static void testExit() { + Process process = new Process("./process_test", + const ["1", "1", "99", "0"]); + final int BUFFERSIZE = 10; + final int STARTCHAR = 65; + Array buffer = new Array(BUFFERSIZE); + for (int i = 0; (i < BUFFERSIZE - 1); i++) { + buffer[i] = STARTCHAR + i; + } + buffer[BUFFERSIZE - 1] = 10; + + SocketInputStream input = process.stderrStream; + SocketOutputStream output = process.stdinStream; + + process.start(); + + Array readBuffer = new Array(BUFFERSIZE); + + void dataWritten() { + void readData() { + for (int i = 0; i < BUFFERSIZE; i++) { + Expect.equals(buffer[i], readBuffer[i]); + } + process.close(); + } + + bool read = input.read(readBuffer, 0, BUFFERSIZE, readData); + if (read) { + readData(); + } + } + bool written = output.write(buffer, 0, BUFFERSIZE, dataWritten); + if (written) { + dataWritten(); + } + } + + static void testMain() { + testExit(); + } +} + +main() { + ProcessStderrTest.testMain(); +} diff --git a/runtime/tests/dart/src/ProcessStdoutTest.dart b/runtime/tests/dart/src/ProcessStdoutTest.dart new file mode 100644 index 00000000000..233f1b33fe6 --- /dev/null +++ b/runtime/tests/dart/src/ProcessStdoutTest.dart @@ -0,0 +1,55 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Process test program to test process communication. + +class ProcessStdoutTest { + + static void testExit() { + Process process = new Process("./process_test", + const ["0", "1", "99", "0"]); + final int BUFFERSIZE = 10; + final int STARTCHAR = 65; + Array buffer = new Array(BUFFERSIZE); + for (int i = 0; (i < BUFFERSIZE - 1); i++) { + buffer[i] = STARTCHAR + i; + } + buffer[BUFFERSIZE - 1] = 10; + + SocketInputStream input = process.stdoutStream; + SocketOutputStream output = process.stdinStream; + + process.start(); + + Array readBuffer = new Array(BUFFERSIZE); + + void dataWritten() { + print("data written"); + void readData() { + print("data read"); + for (int i = 0; i < BUFFERSIZE; i++) { + Expect.equals(buffer[i], readBuffer[i]); + } + process.close(); + } + + bool read = input.read(readBuffer, 0, BUFFERSIZE, readData); + if (read) { + readData(); + } + } + bool written = output.write(buffer, 0, BUFFERSIZE, dataWritten); + if (written) { + dataWritten(); + } + } + + static void testMain() { + testExit(); + } +} + +main() { + ProcessStdoutTest.testMain(); +} diff --git a/runtime/tests/dart/src/SocketCloseTest.dart b/runtime/tests/dart/src/SocketCloseTest.dart new file mode 100644 index 00000000000..5c81bb74763 --- /dev/null +++ b/runtime/tests/dart/src/SocketCloseTest.dart @@ -0,0 +1,172 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Test socket close events. + +class SocketCloseTest { + + static void testMain() { + SocketClose socketClose = new SocketClose.start(); + } +} + +class SocketClose { + + static final SERVERINIT = 0; + static final SERVERSHUTDOWN = -1; + static final ITERATIONS = 100; + + SocketClose.start() + : _receivePort = new ReceivePort(), + _sendPort = null, + _dataEvents = 0, + _closeEvents = 0, + _errorEvents = 0, + _iterations = 0 { + new SocketCloseServer().spawn().then((SendPort port) { + _sendPort = port; + start(); + }); + } + + void sendData() { + + void dataHandler() { + _dataEvents++; + } + + void closeHandler() { + _closeEvents++; + _iterations++; + _socket.close(); + if (_iterations < ITERATIONS) { + sendData(); + } else { + shutdown(); + } + } + + void errorHandler() { + _errorEvents++; + _socket.close(); + } + + void connectHandler() { + _socket.setDataHandler(dataHandler); + _socket.setCloseHandler(closeHandler); + _socket.setErrorHandler(errorHandler); + + if ((_iterations % 2) == 0) { + _socket.writeList("Hello".charCodes(), 0, 5); + } + } + + _socket = new Socket(SocketCloseServer.HOST, _port); + Expect.equals(true, _socket !== null); + _socket.setConnectHandler(connectHandler); + } + + void start() { + _receivePort.receive((var message, SendPort replyTo) { + _port = message; + sendData(); + }); + _sendPort.send(SERVERINIT, _receivePort.toSendPort()); + } + + void shutdown() { + _sendPort.send(SERVERSHUTDOWN, _receivePort.toSendPort()); + _receivePort.close(); + + /* + * Note that it is not guaranteed that _dataEvents == 0 due to spurious + * wakeups. + */ + Expect.equals(ITERATIONS, _closeEvents); + Expect.equals(0, _errorEvents); + } + + int _port; + ReceivePort _receivePort; + SendPort _sendPort; + Socket _socket; + List _buffer; + int _dataEvents; + int _closeEvents; + int _errorEvents; + int _iterations; +} + +class SocketCloseServer extends Isolate { + + static final HOST = "127.0.0.1"; + + SocketCloseServer() : super() {} + + void main() { + + void connectionHandler() { + + void messageHandler() { + _dataEvents++; + _client.close(); + } + + void closeHandler() { + _closeEvents++; + _socket.close(); + } + + void errorHandler() { + _errorEvents++; + _socket.close(); + } + + _client = _server.accept(); + if ((_iterations % 2) == 1) { + _client.close(); + } + _client.setDataHandler(messageHandler); + _client.setCloseHandler(closeHandler); + _client.setErrorHandler(errorHandler); + _iterations++; + } + + void errorHandlerServer() { + _server.close(); + } + + this.port.receive((message, SendPort replyTo) { + if (message == SocketClose.SERVERINIT) { + _errorEvents = 0; + _dataEvents = 0; + _closeEvents = 0; + _iterations = 0; + _server = new ServerSocket(HOST, 0, 10); + Expect.equals(true, _server !== null); + _server.setConnectionHandler(connectionHandler); + _server.setErrorHandler(errorHandlerServer); + replyTo.send(_server.port, null); + } else if (message == SocketClose.SERVERSHUTDOWN) { + Expect.equals(SocketClose.ITERATIONS/2, _dataEvents); + Expect.equals(0, _closeEvents); + Expect.equals(0, _errorEvents); + _server.close(); + this.port.close(); + } + }); + } + + ServerSocket _server; + Socket _client; + int _errorEvents; + int _dataEvents; + int _closeEvents; + int _iterations; +} + + +main() { + SocketCloseTest.testMain(); +} diff --git a/runtime/tests/dart/src/SocketExceptionTest.dart b/runtime/tests/dart/src/SocketExceptionTest.dart new file mode 100644 index 00000000000..b977b8e896e --- /dev/null +++ b/runtime/tests/dart/src/SocketExceptionTest.dart @@ -0,0 +1,188 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Tests socket exceptions. + +class SocketExceptionTest { + + static final PORT = 0; + static final HOST = "127.0.0.1"; + + static void serverSocketExceptionTest() { + bool exceptionCaught = false; + bool wrongExceptionCaught = false; + + ServerSocket server = new ServerSocket(HOST, PORT, 10); + Expect.equals(true, server !== null); + server.close(); + try { + server.close(); + } catch (SocketIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + server.accept(); + } catch (SocketIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + } + + static void clientSocketExceptionTest() { + bool exceptionCaught = false; + bool wrongExceptionCaught = false; + + ServerSocket server = new ServerSocket(HOST, PORT, 10); + Expect.equals(true, server !== null); + int port = server.port; + Socket client = new Socket(HOST, port); + Expect.equals(true, client !== null); + InputStream input = client.inputStream; + OutputStream output = client.outputStream; + client.close(); + try { + client.close(); + } catch (SocketIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + client.available(); + } catch (SocketIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(10); + client.readList(buffer, 0 , 10); + } catch (SocketIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(10); + client.writeList(buffer, 0, 10); + } catch (SocketIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(42); + bool readDone = input.read(buffer, 0, 12, null); + } catch (SocketIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + try { + List buffer = new List(42); + bool readDone = output.write(buffer, 0, 12, null); + } catch (SocketIOException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + + server.close(); + } + + static void indexOutOfRangeExceptionTest() { + bool exceptionCaught = false; + bool wrongExceptionCaught = false; + + ServerSocket server = new ServerSocket(HOST, PORT, 10); + Expect.equals(true, server !== null); + int port = server.port; + Socket client = new Socket(HOST, port); + Expect.equals(true, client !== null); + try { + List buffer = new List(10); + client.readList(buffer, -1, 1); + } catch (IndexOutOfRangeException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + + try { + List buffer = new List(10); + client.readList(buffer, 0, -1); + } catch (IndexOutOfRangeException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + + try { + List buffer = new List(10); + client.writeList(buffer, -1, 1); + } catch (IndexOutOfRangeException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + exceptionCaught = false; + + try { + List buffer = new List(10); + client.writeList(buffer, 0, -1); + } catch (IndexOutOfRangeException ex) { + exceptionCaught = true; + } catch (Exception ex) { + wrongExceptionCaught = true; + } + Expect.equals(true, exceptionCaught); + Expect.equals(true, !wrongExceptionCaught); + + server.close(); + client.close(); + } + + static void testMain() { + serverSocketExceptionTest(); + clientSocketExceptionTest(); + indexOutOfRangeExceptionTest(); + } +} + +main() { + SocketExceptionTest.testMain(); +} diff --git a/runtime/tests/dart/src/StringBaseTest.dart b/runtime/tests/dart/src/StringBaseTest.dart new file mode 100644 index 00000000000..c765313d484 --- /dev/null +++ b/runtime/tests/dart/src/StringBaseTest.dart @@ -0,0 +1,95 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Dart test program for testing class 'StringBase' (currently VM specific). +// VMOptions=--expose_core_impl + +class StringBaseTest { + + StringBaseTest() {} + + toString() { + return "StringBase Tester"; + } + static testSubstringMatches() { + Expect.equals(true, "Hello".substringMatches(0, "Hello")); + Expect.equals(true, "Hello World".substringMatches(0, "Hello")); + Expect.equals(false, "My Hello World".substringMatches(0, "Hello")); + Expect.equals(true, "My Hello World".substringMatches(6, "lo W")); + Expect.equals(false, "Hello".substringMatches(0, "low")); + } + + static testInterpolation() { + Expect.equals("", StringBase._interpolate([])); + Expect.equals("Hello World", + StringBase._interpolate(["Hello", " ", "World"])); + Expect.equals("Hello StringBase Tester!", + StringBase._interpolate(["Hello ", new StringBaseTest(), "!"])); + + var answer = 40 + 2; + var s = "The answer is $answer."; + Expect.equals("The answer is 42.", s); + + int numBottles = 99; + String wall = "wall"; + } + + static testCreation() { + String s = "Hello"; + List a = new List(s.length); + List ga = new List(); + bool exception_caught = false; + for (int i = 0; i < a.length; i++) { + a[i] = s.charCodeAt(i); + ga.add(s.charCodeAt(i)); + } + String s2 = StringBase.createFromCharCodes(a); + Expect.equals(s, s2); + String s3 = StringBase.createFromCharCodes(ga); + Expect.equals(s, s3); + try { + String s4 = new String.fromCharCodes([0.0]); + } catch (IllegalArgumentException ex) { + exception_caught = true; + } + Expect.equals(true, exception_caught); + exception_caught = false; + try { + String s4 = new String.fromCharCodes([-1]); + } catch (IllegalArgumentException ex) { + exception_caught = true; + } + Expect.equals(true, exception_caught); + } + + static testSubstring() { + String s = "Hello World"; + Expect.equals("World", s.substring(6, s.length)); + Expect.equals("", s.substring(8, 8)); + bool exception_caught = false; + try { + s.substring(5, 12); + } catch (IndexOutOfRangeException ex) { + exception_caught = true; + } + Expect.equals(true, exception_caught); + exception_caught = false; + try { + s.substring(5, 4); + } catch (IndexOutOfRangeException ex) { + exception_caught = true; + } + Expect.equals(true, exception_caught); + } + + static void testMain() { + testSubstringMatches(); + testInterpolation(); + testCreation(); + testSubstring(); + } +} + +main() { + StringBaseTest.testMain(); +} diff --git a/runtime/tests/dart/src/TimerCancelTest.dart b/runtime/tests/dart/src/TimerCancelTest.dart new file mode 100644 index 00000000000..31ba336c643 --- /dev/null +++ b/runtime/tests/dart/src/TimerCancelTest.dart @@ -0,0 +1,41 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class TimerCancelTest { + + static void testSimpleTimer() { + + void timeoutHandlerUnreachable(Timer timer) { + Expect.equals(true, false); + } + + void timeoutHandler(Timer timer) { + cancelTimer.cancel(); + } + + void timeoutHandlerRepeat(Timer timer) { + repeatTimer++; + timer.cancel(); + Expect.equals(true, repeatTimer == 1); + } + + cancelTimer = new Timer(timeoutHandlerUnreachable, 1000, false); + cancelTimer.cancel(); + new Timer(timeoutHandler, 1000, false); + cancelTimer = new Timer(timeoutHandlerUnreachable, 2000, false); + repeatTimer = 0; + new Timer(timeoutHandlerRepeat, 1500, true); + } + + static void testMain() { + testSimpleTimer(); + } + + static Timer cancelTimer; + static int repeatTimer; +} + +main() { + TimerCancelTest.testMain(); +} diff --git a/runtime/tests/dart/src/TimerRepeatTest.dart b/runtime/tests/dart/src/TimerRepeatTest.dart new file mode 100644 index 00000000000..dc8374de33f --- /dev/null +++ b/runtime/tests/dart/src/TimerRepeatTest.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class TimerRepeatTest { + + static final int _TIMEOUT = 500; + static final int _ITERATIONS = 5; + + static void testRepeatTimer() { + + void timeoutHandler(Timer timer) { + int endTime = (new DateTime.now()).value; + _iteration++; + if (_iteration < _ITERATIONS) { + _startTime = (new DateTime.now()).value; + } else { + Expect.equals(_iteration, _ITERATIONS); + timer.cancel(); + } + } + + _iteration = 0; + _startTime = (new DateTime.now()).value; + timer = new Timer(timeoutHandler, _TIMEOUT, true); + } + + static void testMain() { + testRepeatTimer(); + } + + static Timer timer; + static int _startTime; + static int _iteration; +} + +main() { + TimerRepeatTest.testMain(); +} diff --git a/runtime/tests/dart/src/TimerTest.dart b/runtime/tests/dart/src/TimerTest.dart new file mode 100644 index 00000000000..360fa0eafff --- /dev/null +++ b/runtime/tests/dart/src/TimerTest.dart @@ -0,0 +1,41 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class TimerTest { + + static final int _STARTTIMEOUT = 1050; + static final int _DECREASE = 200; + static final int _ITERATIONS = 5; + + static void testSimpleTimer() { + + void timeoutHandler(Timer timer) { + int endTime = (new DateTime.now()).value; + Expect.equals(true, (endTime - _startTime) >= _timeout); + if (_iteration < _ITERATIONS) { + _iteration++; + _timeout = _timeout - _DECREASE; + _startTime = (new DateTime.now()).value; + new Timer(timeoutHandler, _timeout, false); + } + } + + _iteration = 0; + _timeout = _STARTTIMEOUT; + _startTime = (new DateTime.now()).value; + new Timer(timeoutHandler, _timeout, false); + } + + static void testMain() { + testSimpleTimer(); + } + + static int _startTime; + static int _timeout; + static int _iteration; +} + +main() { + TimerTest.testMain(); +} diff --git a/runtime/tests/dart/src/readuntil_test.dat b/runtime/tests/dart/src/readuntil_test.dat new file mode 100644 index 00000000000..0c7c848e70f --- /dev/null +++ b/runtime/tests/dart/src/readuntil_test.dat @@ -0,0 +1 @@ +Hello Dart, wassup! diff --git a/runtime/tests/dart/testcfg.py b/runtime/tests/dart/testcfg.py new file mode 100644 index 00000000000..2df39433e47 --- /dev/null +++ b/runtime/tests/dart/testcfg.py @@ -0,0 +1,8 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +import testing + +def GetConfiguration(context, root): + return testing.StandardTestConfiguration(context, root) diff --git a/runtime/tests/vm/data/fixed_length_file b/runtime/tests/vm/data/fixed_length_file new file mode 100644 index 00000000000..52ce32d91b3 --- /dev/null +++ b/runtime/tests/vm/data/fixed_length_file @@ -0,0 +1 @@ +This file should contain exactly 42 bytes. \ No newline at end of file diff --git a/runtime/tests/vm/testcfg.py b/runtime/tests/vm/testcfg.py new file mode 100644 index 00000000000..ea4c1b2495b --- /dev/null +++ b/runtime/tests/vm/testcfg.py @@ -0,0 +1,69 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +import os +import test + +from os.path import join, exists + +class VmTestCase(test.TestCase): + def __init__(self, path, context, mode, arch, flags): + super(VmTestCase, self).__init__(context, path) + self.mode = mode + self.arch = arch + self.flags = flags + + def IsNegative(self): + # TODO(kasperl): Figure out how to support negative tests. Maybe + # just have a TEST_CASE_NEGATIVE macro? + return False + + def GetLabel(self): + return "%s %s" % ( + self.context.GetBuildConf(self.mode, self.arch), + '/'.join(self.path)) + + def GetCommand(self): + command = self.context.GetRunTests(self.mode, self.arch) + command += [ self.GetName() ] + # Add flags being set in the context. + for flag in self.context.flags: + command.append(flag) + if self.flags: command += self.flags + return command + + def GetName(self): + return self.path[-1] + + +class VmTestConfiguration(test.TestConfiguration): + def __init__(self, context, root): + super(VmTestConfiguration, self).__init__(context, root) + + def ListTests(self, current_path, path, mode, arch): + if not arch in ['ia32', 'x64', 'arm', 'simarm']: + return [] + run_tests = self.context.GetRunTests(mode, arch) + output = test.Execute(run_tests + ['--list'], self.context) + if output.exit_code != 0: + print output.stdout + print output.stderr + return [ ] + tests = [ ] + for test_line in output.stdout.strip().split('\n'): + name_and_flags = test_line.split() + name = name_and_flags[0] + flags = name_and_flags[1:] + test_path = current_path + [name] + if self.Contains(path, test_path): + tests.append(VmTestCase(test_path, self.context, mode, arch, flags)) + return tests + + def GetTestStatus(self, sections, defs): + status = join(self.root, 'vm.status') + if exists(status): test.ReadConfigurationInto(status, sections, defs) + + +def GetConfiguration(context, root): + return VmTestConfiguration(context, root) diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status new file mode 100644 index 00000000000..028e3546b4c --- /dev/null +++ b/runtime/tests/vm/vm.status @@ -0,0 +1,6 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +prefix vm + diff --git a/runtime/third_party/jscre/ASCIICType.h b/runtime/third_party/jscre/ASCIICType.h new file mode 100644 index 00000000000..1550b83c718 --- /dev/null +++ b/runtime/third_party/jscre/ASCIICType.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef THIRD_PARTY_JSCRE_ASCIICTYPE_H_ +#define THIRD_PARTY_JSCRE_ASCIICTYPE_H_ + +// The behavior of many of the functions in the header is dependent +// on the current locale. But in the WebKit project, all uses of those functions +// are in code processing something that's not locale-specific. These +// equivalents for some of the functions are named more explicitly, +// not dependent on the C library locale, and we should also optimize them +// as needed. + +// All functions return false or leave the character unchanged if passed +// a character that is outside the range 0-7F. So they can be used on +// Unicode strings or characters if the intent is to do processing only +// if the character is ASCII. + + inline bool isASCIIAlpha(char c) { + return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; + } + inline bool isASCIIAlpha(uint16_t c) { + return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; + } + inline bool isASCIIAlpha(int c) { + return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; + } + + inline bool isASCIIAlphanumeric(char c) { + return (c >= '0' && c <= '9') || + ((c | 0x20) >= 'a' && (c | 0x20) <= 'z'); + } + inline bool isASCIIAlphanumeric(uint16_t c) { + return (c >= '0' && c <= '9') || + ((c | 0x20) >= 'a' && (c | 0x20) <= 'z'); + } + inline bool isASCIIAlphanumeric(int c) { + return (c >= '0' && c <= '9') || + ((c | 0x20) >= 'a' && (c | 0x20) <= 'z'); + } + + inline bool isASCIIDigit(char c) { return (c >= '0') & (c <= '9'); } + inline bool isASCIIDigit(uint16_t c) { + return (c >= '0') & (c <= '9'); + } + inline bool isASCIIDigit(int c) { return (c >= '0') & (c <= '9'); } + + inline bool isASCIIHexDigit(char c) { + return (c >= '0' && c <= '9') || + ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); + } + inline bool isASCIIHexDigit(uint16_t c) { + return (c >= '0' && c <= '9') || + ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); + } + inline bool isASCIIHexDigit(int c) { + return (c >= '0' && c <= '9') || + ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); + } + + inline bool isASCIILower(char c) { return c >= 'a' && c <= 'z'; } + inline bool isASCIILower(uint16_t c) { return c >= 'a' && c <= 'z'; } + inline bool isASCIILower(int c) { return c >= 'a' && c <= 'z'; } + + inline bool isASCIIUpper(char c) { return c >= 'A' && c <= 'Z'; } + inline bool isASCIIUpper(uint16_t c) { return c >= 'A' && c <= 'Z'; } + inline bool isASCIIUpper(int c) { return c >= 'A' && c <= 'Z'; } + + /* + Statistics from a run of Apple's page load test for callers of + isASCIISpace: + + character count + --------- ----- + non-spaces 689383 + 20 space 294720 + 0A \n 89059 + 09 \t 28320 + 0D \r 0 + 0C \f 0 + 0B \v 0 + */ + inline bool isASCIISpace(char c) { + return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); + } + inline bool isASCIISpace(uint16_t c) { + return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); + } + inline bool isASCIISpace(int c) { + return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); + } + + inline char toASCIILower(char c) { + return c | ((c >= 'A' && c <= 'Z') << 5); + } + inline uint16_t toASCIILower(uint16_t c) { + return c | ((c >= 'A' && c <= 'Z') << 5); + } + inline int toASCIILower(int c) { + return c | ((c >= 'A' && c <= 'Z') << 5); + } + + inline char toASCIIUpper(char c) { + return static_cast(c & ~((c >= 'a' && c <= 'z') << 5)); + } + inline uint16_t toASCIIUpper(uint16_t c) { + return static_cast(c & ~((c >= 'a' && c <= 'z') << 5)); + } + inline int toASCIIUpper(int c) { + return static_cast(c & ~((c >= 'a' && c <= 'z') << 5)); + } + + inline int toASCIIHexValue(char c) { + ASSERT(isASCIIHexDigit(c)); + return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; + } + inline int toASCIIHexValue(uint16_t c) { + ASSERT(isASCIIHexDigit(c)); + return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; + } + inline int toASCIIHexValue(int c) { + ASSERT(isASCIIHexDigit(c)); + return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; + } + +#endif // THIRD_PARTY_JSCRE_ASCIICTYPE_H_ diff --git a/runtime/third_party/jscre/AUTHORS b/runtime/third_party/jscre/AUTHORS new file mode 100644 index 00000000000..dbac2a54834 --- /dev/null +++ b/runtime/third_party/jscre/AUTHORS @@ -0,0 +1,12 @@ +Originally written by: Philip Hazel +Email local part: ph10 +Email domain: cam.ac.uk + +University of Cambridge Computing Service, +Cambridge, England. Phone: +44 1223 334714. + +Copyright (c) 1997-2005 University of Cambridge. All rights reserved. + +Adapted for JavaScriptCore and WebKit by Apple Inc. + +Copyright (c) 2005, 2006, 2007 Apple Inc. All rights reserved. diff --git a/runtime/third_party/jscre/COPYING b/runtime/third_party/jscre/COPYING new file mode 100644 index 00000000000..6ffdc24342d --- /dev/null +++ b/runtime/third_party/jscre/COPYING @@ -0,0 +1,35 @@ +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. + +Copyright (c) 1997-2005 University of Cambridge. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the name of Apple + Inc. nor the names of their contributors may be used to endorse or + promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/runtime/third_party/jscre/LICENSE b/runtime/third_party/jscre/LICENSE new file mode 100644 index 00000000000..020c45ff883 --- /dev/null +++ b/runtime/third_party/jscre/LICENSE @@ -0,0 +1,85 @@ +----------------------------------------------------------------------------- +The following license text is extracted from the header of the file +ASCIICType.h and applies only to that file. +----------------------------------------------------------------------------- + +Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- +The following license text is from the file COPYING and applies to the other +source files in this directory. +----------------------------------------------------------------------------- + +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. + +Copyright (c) 1997-2005 University of Cambridge. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the name of Apple + Inc. nor the names of their contributors may be used to endorse or + promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- +The following copyright lines are found in individual files other than +ASCIICType.h +----------------------------------------------------------------------------- + + +Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. +Copyright (C) 2002, 2004, 2006, 2007, 2008 Apple Inc. All rights reserved. +Copyright (C) 2007 Eric Seidel +Copyright (c) 1997-2005 University of Cambridge +Copyright (c) 1997-2005 University of Cambridge. All rights reserved. +Copyright (c) 1997-2006 University of Cambridge +Copyright (c) 2005, 2006, 2007 Apple Inc. All rights reserved. diff --git a/runtime/third_party/jscre/config.h b/runtime/third_party/jscre/config.h new file mode 100644 index 00000000000..5ed9ff1d49b --- /dev/null +++ b/runtime/third_party/jscre/config.h @@ -0,0 +1,185 @@ +/* This is the public header file for JavaScriptCore's variant of the PCRE +library. While this library started out as a copy of PCRE, many of the +features of PCRE have been removed. This library now supports only the +regular expression features required by the JavaScript language +specification, and has only the functions needed by JavaScriptCore and the +rest of WebKit. + + Copyright (c) 1997-2005 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* On Unix-like systems config.in is converted by "configure" into config.h. +Some other environments also support the use of "configure". PCRE is written in +Standard C, but there are a few non-standard things it can cope with, allowing +it to run on SunOS4 and other "close to standard" systems. + +On a non-Unix-like system you should just copy this file into config.h, and set +up the macros the way you need them. You should normally change the definitions +of HAVE_STRERROR and HAVE_MEMMOVE to 1. Unfortunately, because of the way +autoconf works, these cannot be made the defaults. If your system has bcopy() +and not memmove(), change the definition of HAVE_BCOPY instead of HAVE_MEMMOVE. +If your system has neither bcopy() nor memmove(), leave them both as 0; an +emulation function will be used. */ + +/* If you are compiling for a system that uses EBCDIC instead of ASCII +character codes, define this macro as 1. On systems that can use "configure", +this can be done via --enable-ebcdic. */ + +#ifndef THIRD_PARTY_JSCRE_CONFIG_H_ +#define THIRD_PARTY_JSCRE_CONFIG_H_ + +#ifndef EBCDIC +#define EBCDIC 0 +#endif + +/* If you are compiling for a system other than a Unix-like system or Win32, +and it needs some magic to be inserted before the definition of a function that +is exported by the library, define this macro to contain the relevant magic. If +you do not define this macro, it defaults to "extern" for a C compiler and +"extern C" for a C++ compiler on non-Win32 systems. This macro apears at the +start of every exported function that is part of the external API. It does not +appear on functions that are "external" in the C sense, but which are internal +to the library. */ + +/* #define PCRE_DATA_SCOPE */ + +/* Define the following macro to empty if the "const" keyword does not work. */ + +#undef const + +/* Define the following macro to "unsigned" if does not define +size_t. */ + +#undef size_t + +/* The following two definitions are mainly for the benefit of SunOS4, which +does not have the strerror() or memmove() functions that should be present in +all Standard C libraries. The macros HAVE_STRERROR and HAVE_MEMMOVE should +normally be defined with the value 1 for other systems, but unfortunately we +cannot make this the default because "configure" files generated by autoconf +will only change 0 to 1; they won't change 1 to 0 if the functions are not +found. */ + +#define HAVE_STRERROR 1 +#define HAVE_MEMMOVE 1 + +/* There are some non-Unix-like systems that don't even have bcopy(). If this +macro is false, an emulation is used. If HAVE_MEMMOVE is set to 1, the value of +HAVE_BCOPY is not relevant. */ + +#define HAVE_BCOPY 0 + +/* The value of NEWLINE determines the newline character. The default is to +leave it up to the compiler, but some sites want to force a particular value. +On Unix-like systems, "configure" can be used to override this default. */ + +#ifndef NEWLINE +#define NEWLINE '\n' +#endif + +/* The value of LINK_SIZE determines the number of bytes used to store links as +offsets within the compiled regex. The default is 2, which allows for compiled +patterns up to 64K long. This covers the vast majority of cases. However, PCRE +can also be compiled to use 3 or 4 bytes instead. This allows for longer +patterns in extreme cases. On systems that support it, "configure" can be used +to override this default. */ + +#ifndef LINK_SIZE +#define LINK_SIZE 2 +#endif + +/* When calling PCRE via the POSIX interface, additional working storage is +required for holding the pointers to capturing substrings because PCRE requires +three integers per substring, whereas the POSIX interface provides only two. If +the number of expected substrings is small, the wrapper function uses space on +the stack, because this is faster than using malloc() for each call. The +threshold above which the stack is no longer used is defined by POSIX_MALLOC_ +THRESHOLD. On systems that support it, "configure" can be used to override this +default. */ + +#ifndef POSIX_MALLOC_THRESHOLD +#define POSIX_MALLOC_THRESHOLD 10 +#endif + +/* PCRE uses recursive function calls to handle backtracking while matching. +This can sometimes be a problem on systems that have stacks of limited size. +Define NO_RECURSE to get a version that doesn't use recursion in the match() +function; instead it creates its own stack by steam using pcre_recurse_malloc() +to obtain memory from the heap. For more detail, see the comments and other +stuff just above the match() function. On systems that support it, "configure" +can be used to set this in the Makefile (use --disable-stack-for-recursion). */ + +/* #define NO_RECURSE */ + +/* The value of MATCH_LIMIT determines the default number of times the internal +match() function can be called during a single execution of pcre_exec(). There +is a runtime interface for setting a different limit. The limit exists in order +to catch runaway regular expressions that take for ever to determine that they +do not match. The default is set very large so that it does not accidentally +catch legitimate cases. On systems that support it, "configure" can be used to +override this default default. */ + +#ifndef MATCH_LIMIT +#define MATCH_LIMIT 10000000 +#endif + +/* The above limit applies to all calls of match(), whether or not they +increase the recursion depth. In some environments it is desirable to limit the +depth of recursive calls of match() more strictly, in order to restrict the +maximum amount of stack (or heap, if NO_RECURSE is defined) that is used. The +value of MATCH_LIMIT_RECURSION applies only to recursive calls of match(). To +have any useful effect, it must be less than the value of MATCH_LIMIT. There is +a runtime method for setting a different limit. On systems that support it, +"configure" can be used to override this default default. */ + +#ifndef MATCH_LIMIT_RECURSION +#define MATCH_LIMIT_RECURSION MATCH_LIMIT +#endif + +/* These three limits are parameterized just in case anybody ever wants to +change them. Care must be taken if they are increased, because they guard +against integer overflow caused by enormously large patterns. */ + +#ifndef MAX_NAME_SIZE +#define MAX_NAME_SIZE 32 +#endif + +#ifndef MAX_NAME_COUNT +#define MAX_NAME_COUNT 10000 +#endif + +#ifndef MAX_DUPLENGTH +#define MAX_DUPLENGTH 30000 +#endif + +/* End */ +#endif // THIRD_PARTY_JSCRE_CONFIG_H_ diff --git a/runtime/third_party/jscre/jscre.gypi b/runtime/third_party/jscre/jscre.gypi new file mode 100644 index 00000000000..5487a17f401 --- /dev/null +++ b/runtime/third_party/jscre/jscre.gypi @@ -0,0 +1,42 @@ +# Copyright 2010 Google Inc. All Rights Reserved. + +{ + # Needed for compilation with g++ 4.5. + 'conditions': [ + [ 'OS=="linux"', { 'variables' : { + 'common_gcc_warning_flags': [ '-Wno-conversion-null', ], }, } ], + ], + 'includes': [ + '../../../tools/gyp/xcode.gypi', + '../../../tools/gyp/configurations.gypi', + '../../../tools/gyp/source_filter.gypi', + ], + 'targets': [ + { + 'target_name': 'libjscre', + 'type': 'static_library', + 'dependencies': [ + ], + 'include_dirs': [ + '.', + ], + 'defines': [ + 'SUPPORT_UTF8', + 'SUPPORT_UCP', + 'NO_RECURSE', + ], + 'sources': [ + 'ASCIICType.h', + 'config.h', + 'pcre.h', + 'pcre_internal.h', + 'ucpinternal.h', + 'pcre_compile.cpp', + 'pcre_exec.cpp', + 'pcre_tables.cpp', + 'pcre_ucp_searchfuncs.cpp', + 'pcre_xclass.cpp', + ], + }, + ], +} diff --git a/runtime/third_party/jscre/pcre.h b/runtime/third_party/jscre/pcre.h new file mode 100644 index 00000000000..d854fe0ba3f --- /dev/null +++ b/runtime/third_party/jscre/pcre.h @@ -0,0 +1,93 @@ +/* This is the public header file for JavaScriptCore's variant of the PCRE +library. While this library started out as a copy of PCRE, many of the +features of PCRE have been removed. This library now supports only the +regular expression features required by the JavaScript language +specification, and has only the functions needed by JavaScriptCore and the +rest of WebKit. + + Copyright (c) 1997-2005 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +#ifndef THIRD_PARTY_JSCRE_PCRE_H_ +#define THIRD_PARTY_JSCRE_PCRE_H_ + +#if defined(_WIN32) +typedef unsigned __int16 uint16_t; +#else +#include +#include +#endif + +#include +#include +#include +#include +#include + +// JSCRE is very chatty in debug mode, so in order to keep it slient +// while still importing v8.h correctly (it contains #ifdef DEBUGs) +// we allow DEBUG to be set and undef it manually. +#undef DEBUG + +namespace dart { namespace jscre { + +typedef uint16_t UChar; + +struct JSRegExp; +typedef struct JSRegExp JscreRegExp; + +enum JSRegExpIgnoreCaseOption { JSRegExpDoNotIgnoreCase, JSRegExpIgnoreCase }; +enum JSRegExpMultilineOption { JSRegExpSingleLine, JSRegExpMultiline }; + +/* jsRegExpExecute error codes */ +const int JSRegExpErrorNoMatch = -1; +const int JSRegExpErrorHitLimit = -2; +const int JSRegExpErrorNoMemory = -3; +const int JSRegExpErrorInternal = -4; + +typedef void* malloc_t(size_t size); +typedef void free_t(void* address); + +JSRegExp* jsRegExpCompile(const UChar* pattern, int patternLength, + JSRegExpIgnoreCaseOption, JSRegExpMultilineOption, + unsigned* numSubpatterns, const char** errorMessage, + malloc_t* allocate_function, free_t* free_function); + +int jsRegExpExecute(const JSRegExp*, + const UChar* subject, int subjectLength, int startOffset, + int* offsetsVector, int offsetsVectorLength); + +void jsRegExpFree(JSRegExp* regexp); + +} } // namespace dart::jscre + +#endif // THIRD_PARTY_JSCRE_PCRE_H_ diff --git a/runtime/third_party/jscre/pcre_chartables.c b/runtime/third_party/jscre/pcre_chartables.c new file mode 100644 index 00000000000..c5fe2c5ccc0 --- /dev/null +++ b/runtime/third_party/jscre/pcre_chartables.c @@ -0,0 +1,96 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* This file is automatically written by the dftables auxiliary +program. If you edit it by hand, you might like to edit the Makefile to +prevent its ever being regenerated. + +This file contains the default tables for characters with codes less than +128 (ASCII characters). These tables are used when no external tables are +passed to PCRE. */ + +const unsigned char kjs_pcre_default_tables[480] = { + +/* This table is a lower casing table. */ + + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + +/* This table is a case flipping table. */ + + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + +/* This table contains bit maps for various character classes. +Each map is 32 bytes long and the bits run from the least +significant end of each byte. The classes are: space, digit, word. */ + + 0x00, 0x3E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, + 0xFE, 0xFF, 0xFF, 0x87, 0xFE, 0xFF, 0xFF, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + +/* This table identifies various classes of character by individual bits: + 0x01 white space character + 0x08 hexadecimal digit + 0x10 alphanumeric or '_' +*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0- 7 */ + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 8- 15 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 16- 23 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24- 31 */ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* - ' */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ( - / */ + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0 - 7 */ + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8 - ? */ + 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x10, /* @ - G */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, /* H - O */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, /* P - W */ + 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, /* X - _ */ + 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x10, /* ` - g */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, /* h - o */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, /* p - w */ + 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}; /* x -127 */ + + +/* End of chartables.c */ diff --git a/runtime/third_party/jscre/pcre_compile.cpp b/runtime/third_party/jscre/pcre_compile.cpp new file mode 100644 index 00000000000..7da408301da --- /dev/null +++ b/runtime/third_party/jscre/pcre_compile.cpp @@ -0,0 +1,2677 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. + Copyright (C) 2007 Eric Seidel + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This module contains the external function jsRegExpExecute(), along with +supporting internal functions that are not used by other modules. */ + +#include "config.h" + +#include "pcre_internal.h" + +#include +#include "ASCIICType.h" + +/* Negative values for the firstchar and reqchar variables */ + +#define REQ_UNSET (-2) +#define REQ_NONE (-1) + +/************************************************* +* Code parameters and static tables * +*************************************************/ + +/* Maximum number of items on the nested bracket stacks at compile time. This +applies to the nesting of all kinds of parentheses. It does not limit +un-nested, non-capturing parentheses. This number can be made bigger if +necessary - it is used to dimension one int and one unsigned char vector at +compile time. */ + +#define BRASTACK_SIZE 200 + +namespace dart { namespace jscre { + +/* Table for handling escaped characters in the range '0'-'z'. Positive returns +are simple data values; negative values are for special things like \d and so +on. Zero means further processing is needed (for things like \x), or the escape +is invalid. */ + +static const short escapes[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */ + 0, 0, ':', ';', '<', '=', '>', '?', /* 8 - ? */ + '@', 0, -ESC_B, 0, -ESC_D, 0, 0, 0, /* @ - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, -ESC_S, 0, 0, 0, -ESC_W, /* P - W */ + 0, 0, 0, '[', '\\', ']', '^', '_', /* X - _ */ + '`', 7, -ESC_b, 0, -ESC_d, 0, '\f', 0, /* ` - g */ + 0, 0, 0, 0, 0, 0, '\n', 0, /* h - o */ + 0, 0, '\r', -ESC_s, '\t', 0, '\v', -ESC_w, /* p - w */ + 0, 0, 0 /* x - z */ +}; + +/* Error code numbers. They are given names so that they can more easily be +tracked. */ + +enum ErrorCode { + ERR0, ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, + ERR10, ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17 +}; + +/* The texts of compile-time error messages. These are "char *" because they +are passed to the outside world. */ + +static const char* errorText(ErrorCode code) +{ + static const char errorTexts[] = + /* 1 */ + "\\ at end of pattern\0" + "\\c at end of pattern\0" + "character value in \\x{...} sequence is too large\0" + "numbers out of order in {} quantifier\0" + /* 5 */ + "number too big in {} quantifier\0" + "missing terminating ] for character class\0" + "internal error: code overflow\0" + "range out of order in character class\0" + "nothing to repeat\0" + /* 10 */ + "unmatched parentheses\0" + "internal error: unexpected repeat\0" + "unrecognized character after (?\0" + "failed to get memory\0" + "missing )\0" + /* 15 */ + "reference to non-existent subpattern\0" + "regular expression too large\0" + "parentheses nested too deeply" + ; + + int i = code; + const char* text = errorTexts; + while (i > 1) + i -= !*text++; + return text; +} + +/* Structure for passing "static" information around between the functions +doing the compiling. */ + +struct CompileData { + CompileData() { + top_backref = 0; + backrefMap = 0; + req_varyopt = 0; + needOuterBracket = false; + numCapturingBrackets = 0; + } + int top_backref; /* Maximum back reference */ + unsigned backrefMap; /* Bitmap of low back refs */ + int req_varyopt; /* "After variable item" flag for reqbyte */ + bool needOuterBracket; + int numCapturingBrackets; +}; + +/* Definitions to allow mutual recursion */ + +static bool compileBracket(int, int*, unsigned char**, const UChar**, const UChar*, ErrorCode*, int, int*, int*, CompileData&); +static bool bracketIsAnchored(const unsigned char* code); +static bool bracketNeedsLineStart(const unsigned char* code, unsigned captureMap, unsigned backrefMap); +static int bracketFindFirstAssertedCharacter(const unsigned char* code, bool inassert); + +/************************************************* +* Handle escapes * +*************************************************/ + +/* This function is called when a \ has been encountered. It either returns a +positive value for a simple escape such as \n, or a negative value which +encodes one of the more complicated things such as \d. When UTF-8 is enabled, +a positive value greater than 255 may be returned. On entry, ptr is pointing at +the \. On exit, it is on the final character of the escape sequence. + +Arguments: + ptrptr points to the pattern position pointer + errorcodeptr points to the errorcode variable + bracount number of previous extracting brackets + options the options bits + isclass true if inside a character class + +Returns: zero or positive => a data character + negative => a special escape sequence + on error, errorptr is set +*/ + +static int checkEscape(const UChar** ptrptr, const UChar* patternEnd, ErrorCode* errorcodeptr, int bracount, bool isclass) +{ + const UChar* ptr = *ptrptr + 1; + + /* If backslash is at the end of the pattern, it's an error. */ + if (ptr == patternEnd) { + *errorcodeptr = ERR1; + *ptrptr = ptr; + return 0; + } + + int c = *ptr; + + /* Non-alphamerics are literals. For digits or letters, do an initial lookup in + a table. A non-zero result is something that can be returned immediately. + Otherwise further processing may be required. */ + + if (c < '0' || c > 'z') { /* Not alphameric */ + } else if (int escapeValue = escapes[c - '0']) { + c = escapeValue; + if (isclass) { + if (-c == ESC_b) + c = '\b'; /* \b is backslash in a class */ + else if (-c == ESC_B) + c = 'B'; /* and \B is a capital B in a class (in browsers event though ECMAScript 15.10.2.19 says it raises an error) */ + } + /* Escapes that need further processing, or are illegal. */ + + } else { + switch (c) { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* Escape sequences starting with a non-zero digit are backreferences, + unless there are insufficient brackets, in which case they are octal + escape sequences. Those sequences end on the first non-octal character + or when we overflow 0-255, whichever comes first. */ + + if (!isclass) { + const UChar* oldptr = ptr; + c -= '0'; + while ((ptr + 1 < patternEnd) && isASCIIDigit(ptr[1]) && c <= bracount) + c = c * 10 + *(++ptr) - '0'; + if (c <= bracount) { + c = -(ESC_REF + c); + break; + } + ptr = oldptr; /* Put the pointer back and fall through */ + } + + /* Handle an octal number following \. If the first digit is 8 or 9, + this is not octal. */ + + if ((c = *ptr) >= '8') + break; + + /* \0 always starts an octal number, but we may drop through to here with a + larger first octal digit. */ + + case '0': { + c -= '0'; + int i; + for (i = 1; i <= 2; ++i) { + if (ptr + i >= patternEnd || ptr[i] < '0' || ptr[i] > '7') + break; + int cc = c * 8 + ptr[i] - '0'; + if (cc > 255) + break; + c = cc; + } + ptr += i - 1; + break; + } + + case 'x': { + c = 0; + int i; + for (i = 1; i <= 2; ++i) { + if (ptr + i >= patternEnd || !isASCIIHexDigit(ptr[i])) { + c = 'x'; + i = 1; + break; + } + int cc = ptr[i]; + if (cc >= 'a') + cc -= 32; /* Convert to upper case */ + c = c * 16 + cc - ((cc < 'A') ? '0' : ('A' - 10)); + } + ptr += i - 1; + break; + } + + case 'u': { + c = 0; + int i; + for (i = 1; i <= 4; ++i) { + if (ptr + i >= patternEnd || !isASCIIHexDigit(ptr[i])) { + c = 'u'; + i = 1; + break; + } + int cc = ptr[i]; + if (cc >= 'a') + cc -= 32; /* Convert to upper case */ + c = c * 16 + cc - ((cc < 'A') ? '0' : ('A' - 10)); + } + ptr += i - 1; + break; + } + + case 'c': + if (++ptr == patternEnd) { + *errorcodeptr = ERR2; + return 0; + } + c = *ptr; + + /* A letter is upper-cased; then the 0x40 bit is flipped. This coding + is ASCII-specific, but then the whole concept of \cx is ASCII-specific. */ + c = toASCIIUpper(c) ^ 0x40; + break; + } + } + + *ptrptr = ptr; + return c; +} + +/************************************************* +* Check for counted repeat * +*************************************************/ + +/* This function is called when a '{' is encountered in a place where it might +start a quantifier. It looks ahead to see if it really is a quantifier or not. +It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd} +where the ddds are digits. + +Arguments: + p pointer to the first char after '{' + +Returns: true or false +*/ + +static bool isCountedRepeat(const UChar* p, const UChar* patternEnd) +{ + if (p >= patternEnd || !isASCIIDigit(*p)) + return false; + p++; + while (p < patternEnd && isASCIIDigit(*p)) + p++; + if (p < patternEnd && *p == '}') + return true; + + if (p >= patternEnd || *p++ != ',') + return false; + if (p < patternEnd && *p == '}') + return true; + + if (p >= patternEnd || !isASCIIDigit(*p)) + return false; + p++; + while (p < patternEnd && isASCIIDigit(*p)) + p++; + + return (p < patternEnd && *p == '}'); +} + +/************************************************* +* Read repeat counts * +*************************************************/ + +/* Read an item of the form {n,m} and return the values. This is called only +after isCountedRepeat() has confirmed that a repeat-count quantifier exists, +so the syntax is guaranteed to be correct, but we need to check the values. + +Arguments: + p pointer to first char after '{' + minp pointer to int for min + maxp pointer to int for max + returned as -1 if no max + errorcodeptr points to error code variable + +Returns: pointer to '}' on success; + current ptr on error, with errorcodeptr set non-zero +*/ + +static const UChar* readRepeatCounts(const UChar* p, int* minp, int* maxp, ErrorCode* errorcodeptr) +{ + int min = 0; + int max = -1; + + /* Read the minimum value and do a paranoid check: a negative value indicates + an integer overflow. */ + + while (isASCIIDigit(*p)) + min = min * 10 + *p++ - '0'; + if (min < 0 || min > 65535) { + *errorcodeptr = ERR5; + return p; + } + + /* Read the maximum value if there is one, and again do a paranoid on its size. + Also, max must not be less than min. */ + + if (*p == '}') + max = min; + else { + if (*(++p) != '}') { + max = 0; + while (isASCIIDigit(*p)) + max = max * 10 + *p++ - '0'; + if (max < 0 || max > 65535) { + *errorcodeptr = ERR5; + return p; + } + if (max < min) { + *errorcodeptr = ERR4; + return p; + } + } + } + + /* Fill in the required variables, and pass back the pointer to the terminating + '}'. */ + + *minp = min; + *maxp = max; + return p; +} + +/************************************************* +* Find first significant op code * +*************************************************/ + +/* This is called by several functions that scan a compiled expression looking +for a fixed first character, or an anchoring op code etc. It skips over things +that do not influence this. + +Arguments: + code pointer to the start of the group +Returns: pointer to the first significant opcode +*/ + +static const unsigned char* firstSignificantOpcode(const unsigned char* code) +{ + while (*code == OP_BRANUMBER) + code += 3; + return code; +} + +static const unsigned char* firstSignificantOpcodeSkippingAssertions(const unsigned char* code) +{ + while (true) { + switch (*code) { + case OP_ASSERT_NOT: + advanceToEndOfBracket(code); + code += 1 + LINK_SIZE; + break; + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + ++code; + break; + case OP_BRANUMBER: + code += 3; + break; + default: + return code; + } + } +} + +/************************************************* +* Get othercase range * +*************************************************/ + +/* This function is passed the start and end of a class range, in UTF-8 mode +with UCP support. It searches up the characters, looking for internal ranges of +characters in the "other" case. Each call returns the next one, updating the +start address. + +Arguments: + cptr points to starting character value; updated + d end value + ocptr where to put start of othercase range + odptr where to put end of othercase range + +Yield: true when range returned; false when no more +*/ + +static bool getOthercaseRange(int* cptr, int d, int* ocptr, int* odptr) +{ + int c, othercase = 0; + + for (c = *cptr; c <= d; c++) { + if ((othercase = kjs_pcre_ucp_othercase(c)) >= 0) + break; + } + + if (c > d) + return false; + + *ocptr = othercase; + int next = othercase + 1; + + for (++c; c <= d; c++) { + if (kjs_pcre_ucp_othercase(c) != next) + break; + next++; + } + + *odptr = next - 1; + *cptr = c; + + return true; +} + +/************************************************* + * Convert character value to UTF-8 * + *************************************************/ + +/* This function takes an integer value in the range 0 - 0x7fffffff + and encodes it as a UTF-8 character in 0 to 6 bytes. + + Arguments: + cvalue the character value + buffer pointer to buffer for result - at least 6 bytes long + + Returns: number of characters placed in the buffer + */ + +static int encodeUTF8(int cvalue, unsigned char *buffer) +{ + int i; + for (i = 0; i < kjs_pcre_utf8_table1_size; i++) + if (cvalue <= kjs_pcre_utf8_table1[i]) + break; + buffer += i; + for (int j = i; j > 0; j--) { + *buffer-- = 0x80 | (cvalue & 0x3f); + cvalue >>= 6; + } + *buffer = kjs_pcre_utf8_table2[i] | cvalue; + return i + 1; +} + +/************************************************* +* Compile one branch * +*************************************************/ + +/* Scan the pattern, compiling it into the code vector. + +Arguments: + options the option bits + brackets points to number of extracting brackets used + codeptr points to the pointer to the current code point + ptrptr points to the current pattern pointer + errorcodeptr points to error code variable + firstbyteptr set to initial literal character, or < 0 (REQ_UNSET, REQ_NONE) + reqbyteptr set to the last literal character required, else < 0 + cd contains pointers to tables etc. + +Returns: true on success + false, with *errorcodeptr set non-zero on error +*/ + +static inline bool safelyCheckNextChar(const UChar* ptr, const UChar* patternEnd, UChar expected) +{ + return ((ptr + 1 < patternEnd) && ptr[1] == expected); +} + +static bool +compileBranch(int options, int* brackets, unsigned char** codeptr, + const UChar** ptrptr, const UChar* patternEnd, ErrorCode* errorcodeptr, int *firstbyteptr, + int* reqbyteptr, CompileData& cd) +{ + int repeat_type, op_type; + int repeat_min = 0, repeat_max = 0; /* To please picky compilers */ + int bravalue = 0; + int reqvary, tempreqvary; + int c; + unsigned char* code = *codeptr; + unsigned char* tempcode; + bool groupsetfirstbyte = false; + const UChar* ptr = *ptrptr; + const UChar* tempptr; + unsigned char* previous = NULL; + unsigned char classbits[32]; + + bool class_utf8; + unsigned char* class_utf8data; + unsigned char utf8_char[6]; + + /* Initialize no first byte, no required byte. REQ_UNSET means "no char + matching encountered yet". It gets changed to REQ_NONE if we hit something that + matches a non-fixed char first char; reqbyte just remains unset if we never + find one. + + When we hit a repeat whose minimum is zero, we may have to adjust these values + to take the zero repeat into account. This is implemented by setting them to + zerofirstbyte and zeroreqbyte when such a repeat is encountered. The individual + item types that can be repeated set these backoff variables appropriately. */ + + int firstbyte = REQ_UNSET; + int reqbyte = REQ_UNSET; + int zeroreqbyte = REQ_UNSET; + int zerofirstbyte = REQ_UNSET; + + /* The variable req_caseopt contains either the REQ_IGNORE_CASE value or zero, + according to the current setting of the ignores-case flag. REQ_IGNORE_CASE is a bit + value > 255. It is added into the firstbyte or reqbyte variables to record the + case status of the value. This is used only for ASCII characters. */ + + int req_caseopt = (options & IgnoreCaseOption) ? REQ_IGNORE_CASE : 0; + + /* Switch on next character until the end of the branch */ + + for (;; ptr++) { + bool negate_class; + bool should_flip_negation; /* If a negative special such as \S is used, we should negate the whole class to properly support Unicode. */ + int class_charcount; + int class_lastchar; + int skipbytes; + int subreqbyte; + int subfirstbyte; + int mclength; + unsigned char mcbuffer[8]; + + /* Next byte in the pattern */ + + c = ptr < patternEnd ? *ptr : 0; + + /* Fill in length of a previous callout, except when the next thing is + a quantifier. */ + + bool is_quantifier = c == '*' || c == '+' || c == '?' || (c == '{' && isCountedRepeat(ptr + 1, patternEnd)); + + switch (c) { + /* The branch terminates at end of string, |, or ). */ + + case 0: + if (ptr < patternEnd) + goto NORMAL_CHAR; + // End of string; fall through + case '|': + case ')': + *firstbyteptr = firstbyte; + *reqbyteptr = reqbyte; + *codeptr = code; + *ptrptr = ptr; + return true; + + /* Handle single-character metacharacters. In multiline mode, ^ disables + the setting of any following char as a first character. */ + + case '^': + if (options & MatchAcrossMultipleLinesOption) { + if (firstbyte == REQ_UNSET) + firstbyte = REQ_NONE; + *code++ = OP_BOL; + } else + *code++ = OP_CIRC; + previous = NULL; + break; + + case '$': + previous = NULL; + if (options & MatchAcrossMultipleLinesOption) + *code++ = OP_EOL; + else + *code++ = OP_DOLL; + break; + + /* There can never be a first char if '.' is first, whatever happens about + repeats. The value of reqbyte doesn't change either. */ + + case '.': + if (firstbyte == REQ_UNSET) + firstbyte = REQ_NONE; + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; + previous = code; + *code++ = OP_NOT_NEWLINE; + break; + + /* Character classes. If the included characters are all < 256, we build a + 32-byte bitmap of the permitted characters, except in the special case + where there is only one such character. For negated classes, we build the + map as usual, then invert it at the end. However, we use a different opcode + so that data characters > 255 can be handled correctly. + + If the class contains characters outside the 0-255 range, a different + opcode is compiled. It may optionally have a bit map for characters < 256, + but those above are are explicitly listed afterwards. A flag byte tells + whether the bitmap is present, and whether this is a negated class or not. + */ + + case '[': { + previous = code; + should_flip_negation = false; + + /* PCRE supports POSIX class stuff inside a class. Perl gives an error if + they are encountered at the top level, so we'll do that too. */ + + /* If the first character is '^', set the negation flag and skip it. */ + + if (ptr + 1 >= patternEnd) { + *errorcodeptr = ERR6; + return false; + } + + if (ptr[1] == '^') { + negate_class = true; + ++ptr; + } else + negate_class = false; + + /* Keep a count of chars with values < 256 so that we can optimize the case + of just a single character (as long as it's < 256). For higher valued UTF-8 + characters, we don't yet do any optimization. */ + + class_charcount = 0; + class_lastchar = -1; + + class_utf8 = false; /* No chars >= 256 */ + class_utf8data = code + LINK_SIZE + 34; /* For UTF-8 items */ + + /* Initialize the 32-char bit map to all zeros. We have to build the + map in a temporary bit of store, in case the class contains only 1 + character (< 256), because in that case the compiled code doesn't use the + bit map. */ + + memset(classbits, 0, 32 * sizeof(unsigned char)); + + /* Process characters until ] is reached. The first pass + through the regex checked the overall syntax, so we don't need to be very + strict here. At the start of the loop, c contains the first byte of the + character. */ + + while ((++ptr < patternEnd) && (c = *ptr) != ']') { + /* Backslash may introduce a single character, or it may introduce one + of the specials, which just set a flag. Escaped items are checked for + validity in the pre-compiling pass. The sequence \b is a special case. + Inside a class (and only there) it is treated as backspace. Elsewhere + it marks a word boundary. Other escapes have preset maps ready to + or into the one we are building. We assume they have more than one + character in them, so set class_charcount bigger than one. */ + + if (c == '\\') { + c = checkEscape(&ptr, patternEnd, errorcodeptr, cd.numCapturingBrackets, true); + if (c < 0) { + class_charcount += 2; /* Greater than 1 is what matters */ + switch (-c) { + case ESC_d: + for (c = 0; c < 32; c++) + classbits[c] |= classBitmapForChar(c + cbit_digit); + continue; + + case ESC_D: + should_flip_negation = true; + for (c = 0; c < 32; c++) + classbits[c] |= ~classBitmapForChar(c + cbit_digit); + continue; + + case ESC_w: + for (c = 0; c < 32; c++) + classbits[c] |= classBitmapForChar(c + cbit_word); + continue; + + case ESC_W: + should_flip_negation = true; + for (c = 0; c < 32; c++) + classbits[c] |= ~classBitmapForChar(c + cbit_word); + continue; + + case ESC_s: + for (c = 0; c < 32; c++) + classbits[c] |= classBitmapForChar(c + cbit_space); + continue; + + case ESC_S: + should_flip_negation = true; + for (c = 0; c < 32; c++) + classbits[c] |= ~classBitmapForChar(c + cbit_space); + continue; + + /* Unrecognized escapes are faulted if PCRE is running in its + strict mode. By default, for compatibility with Perl, they are + treated as literals. */ + + default: + c = *ptr; /* The final character */ + class_charcount -= 2; /* Undo the default count from above */ + } + } + + /* Fall through if we have a single character (c >= 0). This may be + > 256 in UTF-8 mode. */ + + } /* End of backslash handling */ + + /* A single character may be followed by '-' to form a range. However, + Perl does not permit ']' to be the end of the range. A '-' character + here is treated as a literal. */ + + if ((ptr + 2 < patternEnd) && ptr[1] == '-' && ptr[2] != ']') { + ptr += 2; + + int d = *ptr; + + /* The second part of a range can be a single-character escape, but + not any of the other escapes. Perl 5.6 treats a hyphen as a literal + in such circumstances. */ + + if (d == '\\') { + const UChar* oldptr = ptr; + d = checkEscape(&ptr, patternEnd, errorcodeptr, cd.numCapturingBrackets, true); + + /* \X is literal X; any other special means the '-' was literal */ + if (d < 0) { + ptr = oldptr - 2; + goto LONE_SINGLE_CHARACTER; /* A few lines below */ + } + } + + /* The check that the two values are in the correct order happens in + the pre-pass. Optimize one-character ranges */ + + if (d == c) + goto LONE_SINGLE_CHARACTER; /* A few lines below */ + + /* In UTF-8 mode, if the upper limit is > 255, or > 127 for caseless + matching, we have to use an XCLASS with extra data items. Caseless + matching for characters > 127 is available only if UCP support is + available. */ + + if ((d > 255 || ((options & IgnoreCaseOption) && d > 127))) { + class_utf8 = true; + + /* With UCP support, we can find the other case equivalents of + the relevant characters. There may be several ranges. Optimize how + they fit with the basic range. */ + + if (options & IgnoreCaseOption) { + int occ, ocd; + int cc = c; + int origd = d; + while (getOthercaseRange(&cc, origd, &occ, &ocd)) { + if (occ >= c && ocd <= d) + continue; /* Skip embedded ranges */ + + if (occ < c && ocd >= c - 1) /* Extend the basic range */ + { /* if there is overlap, */ + c = occ; /* noting that if occ < c */ + continue; /* we can't have ocd > d */ + } /* because a subrange is */ + if (ocd > d && occ <= d + 1) /* always shorter than */ + { /* the basic range. */ + d = ocd; + continue; + } + + if (occ == ocd) + *class_utf8data++ = XCL_SINGLE; + else { + *class_utf8data++ = XCL_RANGE; + class_utf8data += encodeUTF8(occ, class_utf8data); + } + class_utf8data += encodeUTF8(ocd, class_utf8data); + } + } + + /* Now record the original range, possibly modified for UCP caseless + overlapping ranges. */ + + *class_utf8data++ = XCL_RANGE; + class_utf8data += encodeUTF8(c, class_utf8data); + class_utf8data += encodeUTF8(d, class_utf8data); + + /* With UCP support, we are done. Without UCP support, there is no + caseless matching for UTF-8 characters > 127; we can use the bit map + for the smaller ones. */ + + continue; /* With next character in the class */ + } + + /* We use the bit map for all cases when not in UTF-8 mode; else + ranges that lie entirely within 0-127 when there is UCP support; else + for partial ranges without UCP support. */ + + for (; c <= d; c++) { + classbits[c/8] |= (1 << (c&7)); + if (options & IgnoreCaseOption) { + int uc = flipCase(c); + classbits[uc/8] |= (1 << (uc&7)); + } + class_charcount++; /* in case a one-char range */ + class_lastchar = c; + } + + continue; /* Go get the next char in the class */ + } + + /* Handle a lone single character - we can get here for a normal + non-escape char, or after \ that introduces a single character or for an + apparent range that isn't. */ + + LONE_SINGLE_CHARACTER: + + /* Handle a character that cannot go in the bit map */ + + if ((c > 255 || ((options & IgnoreCaseOption) && c > 127))) { + class_utf8 = true; + *class_utf8data++ = XCL_SINGLE; + class_utf8data += encodeUTF8(c, class_utf8data); + + if (options & IgnoreCaseOption) { + int othercase; + if ((othercase = kjs_pcre_ucp_othercase(c)) >= 0) { + *class_utf8data++ = XCL_SINGLE; + class_utf8data += encodeUTF8(othercase, class_utf8data); + } + } + } else { + /* Handle a single-byte character */ + classbits[c/8] |= (1 << (c&7)); + if (options & IgnoreCaseOption) { + c = flipCase(c); + classbits[c/8] |= (1 << (c&7)); + } + class_charcount++; + class_lastchar = c; + } + } + + /* If class_charcount is 1, we saw precisely one character whose value is + less than 256. In non-UTF-8 mode we can always optimize. In UTF-8 mode, we + can optimize the negative case only if there were no characters >= 128 + because OP_NOT and the related opcodes like OP_NOTSTAR operate on + single-bytes only. This is an historical hangover. Maybe one day we can + tidy these opcodes to handle multi-byte characters. + + The optimization throws away the bit map. We turn the item into a + 1-character OP_CHAR[NC] if it's positive, or OP_NOT if it's negative. Note + that OP_NOT does not support multibyte characters. In the positive case, it + can cause firstbyte to be set. Otherwise, there can be no first char if + this item is first, whatever repeat count may follow. In the case of + reqbyte, save the previous value for reinstating. */ + + if (class_charcount == 1 && (!class_utf8 && (!negate_class || class_lastchar < 128))) { + zeroreqbyte = reqbyte; + + /* The OP_NOT opcode works on one-byte characters only. */ + + if (negate_class) { + if (firstbyte == REQ_UNSET) + firstbyte = REQ_NONE; + zerofirstbyte = firstbyte; + *code++ = OP_NOT; + *code++ = class_lastchar; + break; + } + + /* For a single, positive character, get the value into c, and + then we can handle this with the normal one-character code. */ + + c = class_lastchar; + goto NORMAL_CHAR; + } /* End of 1-char optimization */ + + /* The general case - not the one-char optimization. If this is the first + thing in the branch, there can be no first char setting, whatever the + repeat count. Any reqbyte setting must remain unchanged after any kind of + repeat. */ + + if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; + + /* If there are characters with values > 255, we have to compile an + extended class, with its own opcode. If there are no characters < 256, + we can omit the bitmap. */ + + if (class_utf8 && !should_flip_negation) { + *class_utf8data++ = XCL_END; /* Marks the end of extra data */ + *code++ = OP_XCLASS; + code += LINK_SIZE; + *code = negate_class? XCL_NOT : 0; + + /* If the map is required, install it, and move on to the end of + the extra data */ + + if (class_charcount > 0) { + *code++ |= XCL_MAP; + memcpy(code, classbits, 32); + code = class_utf8data; + } + + /* If the map is not required, slide down the extra data. */ + + else { + int len = class_utf8data - (code + 33); + memmove(code + 1, code + 33, len); + code += len + 1; + } + + /* Now fill in the complete length of the item */ + + putLinkValue(previous + 1, code - previous); + break; /* End of class handling */ + } + + /* If there are no characters > 255, negate the 32-byte map if necessary, + and copy it into the code vector. If this is the first thing in the branch, + there can be no first char setting, whatever the repeat count. Any reqbyte + setting must remain unchanged after any kind of repeat. */ + + *code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS; + if (negate_class) + for (c = 0; c < 32; c++) + code[c] = ~classbits[c]; + else + memcpy(code, classbits, 32); + code += 32; + break; + } + + /* Various kinds of repeat; '{' is not necessarily a quantifier, but this + has been tested above. */ + + case '{': + if (!is_quantifier) + goto NORMAL_CHAR; + ptr = readRepeatCounts(ptr + 1, &repeat_min, &repeat_max, errorcodeptr); + if (*errorcodeptr) + goto FAILED; + goto REPEAT; + + case '*': + repeat_min = 0; + repeat_max = -1; + goto REPEAT; + + case '+': + repeat_min = 1; + repeat_max = -1; + goto REPEAT; + + case '?': + repeat_min = 0; + repeat_max = 1; + + REPEAT: + if (!previous) { + *errorcodeptr = ERR9; + goto FAILED; + } + + if (repeat_min == 0) { + firstbyte = zerofirstbyte; /* Adjust for zero repeat */ + reqbyte = zeroreqbyte; /* Ditto */ + } + + /* Remember whether this is a variable length repeat */ + + reqvary = (repeat_min == repeat_max) ? 0 : REQ_VARY; + + op_type = 0; /* Default single-char op codes */ + + /* Save start of previous item, in case we have to move it up to make space + for an inserted OP_ONCE for the additional '+' extension. */ + /* FIXME: Probably don't need this because we don't use OP_ONCE. */ + + tempcode = previous; + + /* If the next character is '+', we have a possessive quantifier. This + implies greediness, whatever the setting of the PCRE_UNGREEDY option. + If the next character is '?' this is a minimizing repeat, by default, + but if PCRE_UNGREEDY is set, it works the other way round. We change the + repeat type to the non-default. */ + + if (safelyCheckNextChar(ptr, patternEnd, '?')) { + repeat_type = 1; + ptr++; + } else + repeat_type = 0; + + /* If previous was a character match, abolish the item and generate a + repeat item instead. If a char item has a minumum of more than one, ensure + that it is set in reqbyte - it might not be if a sequence such as x{3} is + the first thing in a branch because the x will have gone into firstbyte + instead. */ + + if (*previous == OP_CHAR || *previous == OP_CHAR_IGNORING_CASE) { + /* Deal with UTF-8 characters that take up more than one byte. It's + easier to write this out separately than try to macrify it. Use c to + hold the length of the character in bytes, plus 0x80 to flag that it's a + length rather than a small character. */ + + if (code[-1] & 0x80) { + unsigned char *lastchar = code - 1; + while((*lastchar & 0xc0) == 0x80) + lastchar--; + c = code - lastchar; /* Length of UTF-8 character */ + memcpy(utf8_char, lastchar, c); /* Save the char */ + c |= 0x80; /* Flag c as a length */ + } + else { + c = code[-1]; + if (repeat_min > 1) + reqbyte = c | req_caseopt | cd.req_varyopt; + } + + goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ + } + + else if (*previous == OP_ASCII_CHAR || *previous == OP_ASCII_LETTER_IGNORING_CASE) { + c = previous[1]; + if (repeat_min > 1) + reqbyte = c | req_caseopt | cd.req_varyopt; + goto OUTPUT_SINGLE_REPEAT; + } + + /* If previous was a single negated character ([^a] or similar), we use + one of the special opcodes, replacing it. The code is shared with single- + character repeats by setting opt_type to add a suitable offset into + repeat_type. OP_NOT is currently used only for single-byte chars. */ + + else if (*previous == OP_NOT) { + op_type = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */ + c = previous[1]; + goto OUTPUT_SINGLE_REPEAT; + } + + /* If previous was a character type match (\d or similar), abolish it and + create a suitable repeat item. The code is shared with single-character + repeats by setting op_type to add a suitable offset into repeat_type. */ + + else if (*previous <= OP_NOT_NEWLINE) { + op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ + c = *previous; + + OUTPUT_SINGLE_REPEAT: + int prop_type = -1; + int prop_value = -1; + + unsigned char* oldcode = code; + code = previous; /* Usually overwrite previous item */ + + /* If the maximum is zero then the minimum must also be zero; Perl allows + this case, so we do too - by simply omitting the item altogether. */ + + if (repeat_max == 0) + goto END_REPEAT; + + /* Combine the op_type with the repeat_type */ + + repeat_type += op_type; + + /* A minimum of zero is handled either as the special case * or ?, or as + an UPTO, with the maximum given. */ + + if (repeat_min == 0) { + if (repeat_max == -1) + *code++ = OP_STAR + repeat_type; + else if (repeat_max == 1) + *code++ = OP_QUERY + repeat_type; + else { + *code++ = OP_UPTO + repeat_type; + put2ByteValueAndAdvance(code, repeat_max); + } + } + + /* A repeat minimum of 1 is optimized into some special cases. If the + maximum is unlimited, we use OP_PLUS. Otherwise, the original item it + left in place and, if the maximum is greater than 1, we use OP_UPTO with + one less than the maximum. */ + + else if (repeat_min == 1) { + if (repeat_max == -1) + *code++ = OP_PLUS + repeat_type; + else { + code = oldcode; /* leave previous item in place */ + if (repeat_max == 1) + goto END_REPEAT; + *code++ = OP_UPTO + repeat_type; + put2ByteValueAndAdvance(code, repeat_max - 1); + } + } + + /* The case {n,n} is just an EXACT, while the general case {n,m} is + handled as an EXACT followed by an UPTO. */ + + else { + *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ + put2ByteValueAndAdvance(code, repeat_min); + + /* If the maximum is unlimited, insert an OP_STAR. Before doing so, + we have to insert the character for the previous code. For a repeated + Unicode property match, there are two extra bytes that define the + required property. In UTF-8 mode, long characters have their length in + c, with the 0x80 bit as a flag. */ + + if (repeat_max < 0) { + if (c >= 128) { + memcpy(code, utf8_char, c & 7); + code += c & 7; + } else { + *code++ = c; + if (prop_type >= 0) { + *code++ = prop_type; + *code++ = prop_value; + } + } + *code++ = OP_STAR + repeat_type; + } + + /* Else insert an UPTO if the max is greater than the min, again + preceded by the character, for the previously inserted code. */ + + else if (repeat_max != repeat_min) { + if (c >= 128) { + memcpy(code, utf8_char, c & 7); + code += c & 7; + } else + *code++ = c; + if (prop_type >= 0) { + *code++ = prop_type; + *code++ = prop_value; + } + repeat_max -= repeat_min; + *code++ = OP_UPTO + repeat_type; + put2ByteValueAndAdvance(code, repeat_max); + } + } + + /* The character or character type itself comes last in all cases. */ + + if (c >= 128) { + memcpy(code, utf8_char, c & 7); + code += c & 7; + } else + *code++ = c; + + /* For a repeated Unicode property match, there are two extra bytes that + define the required property. */ + + if (prop_type >= 0) { + *code++ = prop_type; + *code++ = prop_value; + } + } + + /* If previous was a character class or a back reference, we put the repeat + stuff after it, but just skip the item if the repeat was {0,0}. */ + + else if (*previous == OP_CLASS || + *previous == OP_NCLASS || + *previous == OP_XCLASS || + *previous == OP_REF) + { + if (repeat_max == 0) { + code = previous; + goto END_REPEAT; + } + + if (repeat_min == 0 && repeat_max == -1) + *code++ = OP_CRSTAR + repeat_type; + else if (repeat_min == 1 && repeat_max == -1) + *code++ = OP_CRPLUS + repeat_type; + else if (repeat_min == 0 && repeat_max == 1) + *code++ = OP_CRQUERY + repeat_type; + else { + *code++ = OP_CRRANGE + repeat_type; + put2ByteValueAndAdvance(code, repeat_min); + if (repeat_max == -1) + repeat_max = 0; /* 2-byte encoding for max */ + put2ByteValueAndAdvance(code, repeat_max); + } + } + + /* If previous was a bracket group, we may have to replicate it in certain + cases. */ + + else if (*previous >= OP_BRA) { + int ketoffset = 0; + int len = code - previous; + unsigned char* bralink = NULL; + + /* If the maximum repeat count is unlimited, find the end of the bracket + by scanning through from the start, and compute the offset back to it + from the current code pointer. There may be an OP_OPT setting following + the final KET, so we can't find the end just by going back from the code + pointer. */ + + if (repeat_max == -1) { + const unsigned char* ket = previous; + advanceToEndOfBracket(ket); + ketoffset = code - ket; + } + + /* The case of a zero minimum is special because of the need to stick + OP_BRAZERO in front of it, and because the group appears once in the + data, whereas in other cases it appears the minimum number of times. For + this reason, it is simplest to treat this case separately, as otherwise + the code gets far too messy. There are several special subcases when the + minimum is zero. */ + + if (repeat_min == 0) { + /* If the maximum is also zero, we just omit the group from the output + altogether. */ + + if (repeat_max == 0) { + code = previous; + goto END_REPEAT; + } + + /* If the maximum is 1 or unlimited, we just have to stick in the + BRAZERO and do no more at this point. However, we do need to adjust + any OP_RECURSE calls inside the group that refer to the group itself or + any internal group, because the offset is from the start of the whole + regex. Temporarily terminate the pattern while doing this. */ + + if (repeat_max <= 1) { + *code = OP_END; + memmove(previous+1, previous, len); + code++; + *previous++ = OP_BRAZERO + repeat_type; + } + + /* If the maximum is greater than 1 and limited, we have to replicate + in a nested fashion, sticking OP_BRAZERO before each set of brackets. + The first one has to be handled carefully because it's the original + copy, which has to be moved up. The remainder can be handled by code + that is common with the non-zero minimum case below. We have to + adjust the value of repeat_max, since one less copy is required. */ + + else { + *code = OP_END; + memmove(previous + 2 + LINK_SIZE, previous, len); + code += 2 + LINK_SIZE; + *previous++ = OP_BRAZERO + repeat_type; + *previous++ = OP_BRA; + + /* We chain together the bracket offset fields that have to be + filled in later when the ends of the brackets are reached. */ + + int offset = (!bralink) ? 0 : previous - bralink; + bralink = previous; + putLinkValueAllowZeroAndAdvance(previous, offset); + } + + repeat_max--; + } + + /* If the minimum is greater than zero, replicate the group as many + times as necessary, and adjust the maximum to the number of subsequent + copies that we need. If we set a first char from the group, and didn't + set a required char, copy the latter from the former. */ + + else { + if (repeat_min > 1) { + if (groupsetfirstbyte && reqbyte < 0) + reqbyte = firstbyte; + for (int i = 1; i < repeat_min; i++) { + memcpy(code, previous, len); + code += len; + } + } + if (repeat_max > 0) + repeat_max -= repeat_min; + } + + /* This code is common to both the zero and non-zero minimum cases. If + the maximum is limited, it replicates the group in a nested fashion, + remembering the bracket starts on a stack. In the case of a zero minimum, + the first one was set up above. In all cases the repeat_max now specifies + the number of additional copies needed. */ + + if (repeat_max >= 0) { + for (int i = repeat_max - 1; i >= 0; i--) { + *code++ = OP_BRAZERO + repeat_type; + + /* All but the final copy start a new nesting, maintaining the + chain of brackets outstanding. */ + + if (i != 0) { + *code++ = OP_BRA; + int offset = (!bralink) ? 0 : code - bralink; + bralink = code; + putLinkValueAllowZeroAndAdvance(code, offset); + } + + memcpy(code, previous, len); + code += len; + } + + /* Now chain through the pending brackets, and fill in their length + fields (which are holding the chain links pro tem). */ + + while (bralink) { + int offset = code - bralink + 1; + unsigned char* bra = code - offset; + int oldlinkoffset = getLinkValueAllowZero(bra + 1); + bralink = (!oldlinkoffset) ? 0 : bralink - oldlinkoffset; + *code++ = OP_KET; + putLinkValueAndAdvance(code, offset); + putLinkValue(bra + 1, offset); + } + } + + /* If the maximum is unlimited, set a repeater in the final copy. We + can't just offset backwards from the current code point, because we + don't know if there's been an options resetting after the ket. The + correct offset was computed above. */ + + else + code[-ketoffset] = OP_KETRMAX + repeat_type; + } + + /* Else there's some kind of shambles */ + + else { + *errorcodeptr = ERR11; + goto FAILED; + } + + /* In all case we no longer have a previous item. We also set the + "follows varying string" flag for subsequently encountered reqbytes if + it isn't already set and we have just passed a varying length item. */ + + END_REPEAT: + previous = NULL; + cd.req_varyopt |= reqvary; + break; + + /* Start of nested bracket sub-expression, or comment or lookahead or + lookbehind or option setting or condition. First deal with special things + that can come after a bracket; all are introduced by ?, and the appearance + of any of them means that this is not a referencing group. They were + checked for validity in the first pass over the string, so we don't have to + check for syntax errors here. */ + + case '(': + skipbytes = 0; + + if (*(++ptr) == '?') { + switch (*(++ptr)) { + case ':': /* Non-extracting bracket */ + bravalue = OP_BRA; + ptr++; + break; + + case '=': /* Positive lookahead */ + bravalue = OP_ASSERT; + ptr++; + break; + + case '!': /* Negative lookahead */ + bravalue = OP_ASSERT_NOT; + ptr++; + break; + + /* Character after (? not specially recognized */ + + default: + *errorcodeptr = ERR12; + goto FAILED; + } + } + + /* Else we have a referencing group; adjust the opcode. If the bracket + number is greater than EXTRACT_BASIC_MAX, we set the opcode one higher, and + arrange for the true number to follow later, in an OP_BRANUMBER item. */ + + else { + if (++(*brackets) > EXTRACT_BASIC_MAX) { + bravalue = OP_BRA + EXTRACT_BASIC_MAX + 1; + code[1 + LINK_SIZE] = OP_BRANUMBER; + put2ByteValue(code + 2 + LINK_SIZE, *brackets); + skipbytes = 3; + } + else + bravalue = OP_BRA + *brackets; + } + + /* Process nested bracketed re. Assertions may not be repeated, but other + kinds can be. We copy code into a non-variable in order to be able + to pass its address because some compilers complain otherwise. Pass in a + new setting for the ims options if they have changed. */ + + previous = (bravalue >= OP_BRAZERO) ? code : 0; + *code = bravalue; + tempcode = code; + tempreqvary = cd.req_varyopt; /* Save value before bracket */ + + if (!compileBracket( + options, + brackets, /* Extracting bracket count */ + &tempcode, /* Where to put code (updated) */ + &ptr, /* Input pointer (updated) */ + patternEnd, + errorcodeptr, /* Where to put an error message */ + skipbytes, /* Skip over OP_BRANUMBER */ + &subfirstbyte, /* For possible first char */ + &subreqbyte, /* For possible last char */ + cd)) /* Tables block */ + goto FAILED; + + /* At the end of compiling, code is still pointing to the start of the + group, while tempcode has been updated to point past the end of the group + and any option resetting that may follow it. The pattern pointer (ptr) + is on the bracket. */ + + /* Handle updating of the required and first characters. Update for normal + brackets of all kinds, and conditions with two branches (see code above). + If the bracket is followed by a quantifier with zero repeat, we have to + back off. Hence the definition of zeroreqbyte and zerofirstbyte outside the + main loop so that they can be accessed for the back off. */ + + zeroreqbyte = reqbyte; + zerofirstbyte = firstbyte; + groupsetfirstbyte = false; + + if (bravalue >= OP_BRA) { + /* If we have not yet set a firstbyte in this branch, take it from the + subpattern, remembering that it was set here so that a repeat of more + than one can replicate it as reqbyte if necessary. If the subpattern has + no firstbyte, set "none" for the whole branch. In both cases, a zero + repeat forces firstbyte to "none". */ + + if (firstbyte == REQ_UNSET) { + if (subfirstbyte >= 0) { + firstbyte = subfirstbyte; + groupsetfirstbyte = true; + } + else + firstbyte = REQ_NONE; + zerofirstbyte = REQ_NONE; + } + + /* If firstbyte was previously set, convert the subpattern's firstbyte + into reqbyte if there wasn't one, using the vary flag that was in + existence beforehand. */ + + else if (subfirstbyte >= 0 && subreqbyte < 0) + subreqbyte = subfirstbyte | tempreqvary; + + /* If the subpattern set a required byte (or set a first byte that isn't + really the first byte - see above), set it. */ + + if (subreqbyte >= 0) + reqbyte = subreqbyte; + } + + /* For a forward assertion, we take the reqbyte, if set. This can be + helpful if the pattern that follows the assertion doesn't set a different + char. For example, it's useful for /(?=abcde).+/. We can't set firstbyte + for an assertion, however because it leads to incorrect effect for patterns + such as /(?=a)a.+/ when the "real" "a" would then become a reqbyte instead + of a firstbyte. This is overcome by a scan at the end if there's no + firstbyte, looking for an asserted first char. */ + + else if (bravalue == OP_ASSERT && subreqbyte >= 0) + reqbyte = subreqbyte; + + /* Now update the main code pointer to the end of the group. */ + + code = tempcode; + + /* Error if hit end of pattern */ + + if (ptr >= patternEnd || *ptr != ')') { + *errorcodeptr = ERR14; + goto FAILED; + } + break; + + /* Check \ for being a real metacharacter; if not, fall through and handle + it as a data character at the start of a string. Escape items are checked + for validity in the pre-compiling pass. */ + + case '\\': + tempptr = ptr; + c = checkEscape(&ptr, patternEnd, errorcodeptr, cd.numCapturingBrackets, false); + + /* Handle metacharacters introduced by \. For ones like \d, the ESC_ values + are arranged to be the negation of the corresponding OP_values. For the + back references, the values are ESC_REF plus the reference number. Only + back references and those types that consume a character may be repeated. + We can test for values between ESC_b and ESC_w for the latter; this may + have to change if any new ones are ever created. */ + + if (c < 0) { + /* For metasequences that actually match a character, we disable the + setting of a first character if it hasn't already been set. */ + + if (firstbyte == REQ_UNSET && -c > ESC_b && -c <= ESC_w) + firstbyte = REQ_NONE; + + /* Set values to reset to if this is followed by a zero repeat. */ + + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; + + /* Back references are handled specially */ + + if (-c >= ESC_REF) { + int number = -c - ESC_REF; + previous = code; + *code++ = OP_REF; + put2ByteValueAndAdvance(code, number); + } + + /* For the rest, we can obtain the OP value by negating the escape + value */ + + else { + previous = (-c > ESC_b && -c <= ESC_w) ? code : NULL; + *code++ = -c; + } + continue; + } + + /* Fall through. */ + + /* Handle a literal character. It is guaranteed not to be whitespace or # + when the extended flag is set. If we are in UTF-8 mode, it may be a + multi-byte literal character. */ + + default: + NORMAL_CHAR: + + previous = code; + + if (c < 128) { + mclength = 1; + mcbuffer[0] = c; + + if ((options & IgnoreCaseOption) && (c | 0x20) >= 'a' && (c | 0x20) <= 'z') { + *code++ = OP_ASCII_LETTER_IGNORING_CASE; + *code++ = c | 0x20; + } else { + *code++ = OP_ASCII_CHAR; + *code++ = c; + } + } else { + mclength = encodeUTF8(c, mcbuffer); + + *code++ = (options & IgnoreCaseOption) ? OP_CHAR_IGNORING_CASE : OP_CHAR; + for (c = 0; c < mclength; c++) + *code++ = mcbuffer[c]; + } + + /* Set the first and required bytes appropriately. If no previous first + byte, set it from this character, but revert to none on a zero repeat. + Otherwise, leave the firstbyte value alone, and don't change it on a zero + repeat. */ + + if (firstbyte == REQ_UNSET) { + zerofirstbyte = REQ_NONE; + zeroreqbyte = reqbyte; + + /* If the character is more than one byte long, we can set firstbyte + only if it is not to be matched caselessly. */ + + if (mclength == 1 || req_caseopt == 0) { + firstbyte = mcbuffer[0] | req_caseopt; + if (mclength != 1) + reqbyte = code[-1] | cd.req_varyopt; + } + else + firstbyte = reqbyte = REQ_NONE; + } + + /* firstbyte was previously set; we can set reqbyte only the length is + 1 or the matching is caseful. */ + + else { + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; + if (mclength == 1 || req_caseopt == 0) + reqbyte = code[-1] | req_caseopt | cd.req_varyopt; + } + + break; /* End of literal character handling */ + } + } /* end of big loop */ + + /* Control never reaches here by falling through, only by a goto for all the + error states. Pass back the position in the pattern so that it can be displayed + to the user for diagnosing the error. */ + +FAILED: + *ptrptr = ptr; + return false; +} + +/************************************************* +* Compile sequence of alternatives * +*************************************************/ + +/* On entry, ptr is pointing past the bracket character, but on return +it points to the closing bracket, or vertical bar, or end of string. +The code variable is pointing at the byte into which the BRA operator has been +stored. If the ims options are changed at the start (for a (?ims: group) or +during any branch, we need to insert an OP_OPT item at the start of every +following branch to ensure they get set correctly at run time, and also pass +the new options into every subsequent branch compile. + +Argument: + options option bits, including any changes for this subpattern + brackets -> int containing the number of extracting brackets used + codeptr -> the address of the current code pointer + ptrptr -> the address of the current pattern pointer + errorcodeptr -> pointer to error code variable + skipbytes skip this many bytes at start (for OP_BRANUMBER) + firstbyteptr place to put the first required character, or a negative number + reqbyteptr place to put the last required character, or a negative number + cd points to the data block with tables pointers etc. + +Returns: true on success +*/ + +static bool +compileBracket(int options, int* brackets, unsigned char** codeptr, + const UChar** ptrptr, const UChar* patternEnd, ErrorCode* errorcodeptr, int skipbytes, + int* firstbyteptr, int* reqbyteptr, CompileData& cd) +{ + const UChar* ptr = *ptrptr; + unsigned char* code = *codeptr; + unsigned char* last_branch = code; + unsigned char* start_bracket = code; + int firstbyte = REQ_UNSET; + int reqbyte = REQ_UNSET; + + /* Offset is set zero to mark that this bracket is still open */ + + putLinkValueAllowZero(code + 1, 0); + code += 1 + LINK_SIZE + skipbytes; + + /* Loop for each alternative branch */ + + while (true) { + /* Now compile the branch */ + + int branchfirstbyte; + int branchreqbyte; + if (!compileBranch(options, brackets, &code, &ptr, patternEnd, errorcodeptr, + &branchfirstbyte, &branchreqbyte, cd)) { + *ptrptr = ptr; + return false; + } + + /* If this is the first branch, the firstbyte and reqbyte values for the + branch become the values for the regex. */ + + if (*last_branch != OP_ALT) { + firstbyte = branchfirstbyte; + reqbyte = branchreqbyte; + } + + /* If this is not the first branch, the first char and reqbyte have to + match the values from all the previous branches, except that if the previous + value for reqbyte didn't have REQ_VARY set, it can still match, and we set + REQ_VARY for the regex. */ + + else { + /* If we previously had a firstbyte, but it doesn't match the new branch, + we have to abandon the firstbyte for the regex, but if there was previously + no reqbyte, it takes on the value of the old firstbyte. */ + + if (firstbyte >= 0 && firstbyte != branchfirstbyte) { + if (reqbyte < 0) + reqbyte = firstbyte; + firstbyte = REQ_NONE; + } + + /* If we (now or from before) have no firstbyte, a firstbyte from the + branch becomes a reqbyte if there isn't a branch reqbyte. */ + + if (firstbyte < 0 && branchfirstbyte >= 0 && branchreqbyte < 0) + branchreqbyte = branchfirstbyte; + + /* Now ensure that the reqbytes match */ + + if ((reqbyte & ~REQ_VARY) != (branchreqbyte & ~REQ_VARY)) + reqbyte = REQ_NONE; + else + reqbyte |= branchreqbyte; /* To "or" REQ_VARY */ + } + + /* Reached end of expression, either ')' or end of pattern. Go back through + the alternative branches and reverse the chain of offsets, with the field in + the BRA item now becoming an offset to the first alternative. If there are + no alternatives, it points to the end of the group. The length in the + terminating ket is always the length of the whole bracketed item. If any of + the ims options were changed inside the group, compile a resetting op-code + following, except at the very end of the pattern. Return leaving the pointer + at the terminating char. */ + + if (ptr >= patternEnd || *ptr != '|') { + int length = code - last_branch; + do { + int prev_length = getLinkValueAllowZero(last_branch + 1); + putLinkValue(last_branch + 1, length); + length = prev_length; + last_branch -= length; + } while (length > 0); + + /* Fill in the ket */ + + *code = OP_KET; + putLinkValue(code + 1, code - start_bracket); + code += 1 + LINK_SIZE; + + /* Set values to pass back */ + + *codeptr = code; + *ptrptr = ptr; + *firstbyteptr = firstbyte; + *reqbyteptr = reqbyte; + return true; + } + + /* Another branch follows; insert an "or" node. Its length field points back + to the previous branch while the bracket remains open. At the end the chain + is reversed. It's done like this so that the start of the bracket has a + zero offset until it is closed, making it possible to detect recursion. */ + + *code = OP_ALT; + putLinkValue(code + 1, code - last_branch); + last_branch = code; + code += 1 + LINK_SIZE; + ptr++; + } + ASSERT_NOT_REACHED(); +} + +/************************************************* +* Check for anchored expression * +*************************************************/ + +/* Try to find out if this is an anchored regular expression. Consider each +alternative branch. If they all start OP_CIRC, or with a bracket +all of whose alternatives start OP_CIRC (recurse ad lib), then +it's anchored. + +Arguments: + code points to start of expression (the bracket) + captureMap a bitmap of which brackets we are inside while testing; this + handles up to substring 31; all brackets after that share + the zero bit + backrefMap the back reference bitmap +*/ + +static bool branchIsAnchored(const unsigned char* code) +{ + const unsigned char* scode = firstSignificantOpcode(code); + int op = *scode; + + /* Brackets */ + if (op >= OP_BRA || op == OP_ASSERT) + return bracketIsAnchored(scode); + + /* Check for explicit anchoring */ + return op == OP_CIRC; +} + +static bool bracketIsAnchored(const unsigned char* code) +{ + do { + if (!branchIsAnchored(code + 1 + LINK_SIZE)) + return false; + code += getLinkValue(code + 1); + } while (*code == OP_ALT); /* Loop for each alternative */ + return true; +} + +/************************************************* +* Check for starting with ^ or .* * +*************************************************/ + +/* This is called to find out if every branch starts with ^ or .* so that +"first char" processing can be done to speed things up in multiline +matching and for non-DOTALL patterns that start with .* (which must start at +the beginning or after \n) + +Except when the .* appears inside capturing parentheses, and there is a +subsequent back reference to those parentheses. By keeping a bitmap of the +first 31 back references, we can catch some of the more common cases more +precisely; all the greater back references share a single bit. + +Arguments: + code points to start of expression (the bracket) + captureMap a bitmap of which brackets we are inside while testing; this + handles up to substring 31; all brackets after that share + the zero bit + backrefMap the back reference bitmap +*/ + +static bool branchNeedsLineStart(const unsigned char* code, unsigned captureMap, unsigned backrefMap) +{ + const unsigned char* scode = firstSignificantOpcode(code); + int op = *scode; + + /* Capturing brackets */ + if (op > OP_BRA) { + int captureNum = op - OP_BRA; + if (captureNum > EXTRACT_BASIC_MAX) + captureNum = get2ByteValue(scode + 2 + LINK_SIZE); + int bracketMask = (captureNum < 32) ? (1 << captureNum) : 1; + return bracketNeedsLineStart(scode, captureMap | bracketMask, backrefMap); + } + + /* Other brackets */ + if (op == OP_BRA || op == OP_ASSERT) + return bracketNeedsLineStart(scode, captureMap, backrefMap); + + /* .* means "start at start or after \n" if it isn't in brackets that + may be referenced. */ + + if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR) + return scode[1] == OP_NOT_NEWLINE && !(captureMap & backrefMap); + + /* Explicit ^ */ + return op == OP_CIRC || op == OP_BOL; +} + +static bool bracketNeedsLineStart(const unsigned char* code, unsigned captureMap, unsigned backrefMap) +{ + do { + if (!branchNeedsLineStart(code + 1 + LINK_SIZE, captureMap, backrefMap)) + return false; + code += getLinkValue(code + 1); + } while (*code == OP_ALT); /* Loop for each alternative */ + return true; +} + +/************************************************* +* Check for asserted fixed first char * +*************************************************/ + +/* During compilation, the "first char" settings from forward assertions are +discarded, because they can cause conflicts with actual literals that follow. +However, if we end up without a first char setting for an unanchored pattern, +it is worth scanning the regex to see if there is an initial asserted first +char. If all branches start with the same asserted char, or with a bracket all +of whose alternatives start with the same asserted char (recurse ad lib), then +we return that char, otherwise -1. + +Arguments: + code points to start of expression (the bracket) + options pointer to the options (used to check casing changes) + inassert true if in an assertion + +Returns: -1 or the fixed first char +*/ + +static int branchFindFirstAssertedCharacter(const unsigned char* code, bool inassert) +{ + const unsigned char* scode = firstSignificantOpcodeSkippingAssertions(code); + int op = *scode; + + if (op >= OP_BRA) + op = OP_BRA; + + switch (op) { + default: + return -1; + + case OP_BRA: + case OP_ASSERT: + return bracketFindFirstAssertedCharacter(scode, op == OP_ASSERT); + + case OP_EXACT: + scode += 2; + /* Fall through */ + + case OP_CHAR: + case OP_CHAR_IGNORING_CASE: + case OP_ASCII_CHAR: + case OP_ASCII_LETTER_IGNORING_CASE: + case OP_PLUS: + case OP_MINPLUS: + if (!inassert) + return -1; + return scode[1]; + } +} + +static int bracketFindFirstAssertedCharacter(const unsigned char* code, bool inassert) +{ + int c = -1; + do { + int d = branchFindFirstAssertedCharacter(code + 1 + LINK_SIZE, inassert); + if (d < 0) + return -1; + if (c < 0) + c = d; + else if (c != d) + return -1; + code += getLinkValue(code + 1); + } while (*code == OP_ALT); + return c; +} + +static inline int multiplyWithOverflowCheck(int a, int b) +{ + if (!a || !b) + return 0; + if (a > MAX_PATTERN_SIZE / b) + return -1; + return a * b; +} + +static int calculateCompiledPatternLength(const UChar* pattern, int patternLength, JSRegExpIgnoreCaseOption ignoreCase, + CompileData& cd, ErrorCode& errorcode) +{ + /* Make a pass over the pattern to compute the + amount of store required to hold the compiled code. This does not have to be + perfect as long as errors are overestimates. */ + + if (patternLength > MAX_PATTERN_SIZE) { + errorcode = ERR16; + return -1; + } + + int length = 1 + LINK_SIZE; /* For initial BRA plus length */ + int branch_extra = 0; + int lastitemlength = 0; + unsigned brastackptr = 0; + int brastack[BRASTACK_SIZE]; + unsigned char bralenstack[BRASTACK_SIZE]; + int bracount = 0; + + const UChar* ptr = reinterpret_cast(pattern - 1); + const UChar* patternEnd = reinterpret_cast(pattern + patternLength); + + while (++ptr < patternEnd) { + int minRepeats = 0, maxRepeats = 0; + int c = *ptr; + + switch (c) { + /* A backslashed item may be an escaped data character or it may be a + character type. */ + + case '\\': + c = checkEscape(&ptr, patternEnd, &errorcode, cd.numCapturingBrackets, false); + if (errorcode != 0) + return -1; + + lastitemlength = 1; /* Default length of last item for repeats */ + + if (c >= 0) { /* Data character */ + length += 2; /* For a one-byte character */ + + if (c > 127) { + int i; + for (i = 0; i < kjs_pcre_utf8_table1_size; i++) + if (c <= kjs_pcre_utf8_table1[i]) break; + length += i; + lastitemlength += i; + } + + continue; + } + + /* Other escapes need one byte */ + + length++; + + /* A back reference needs an additional 2 bytes, plus either one or 5 + bytes for a repeat. We also need to keep the value of the highest + back reference. */ + + if (c <= -ESC_REF) { + int refnum = -c - ESC_REF; + cd.backrefMap |= (refnum < 32) ? (1 << refnum) : 1; + if (refnum > cd.top_backref) + cd.top_backref = refnum; + length += 2; /* For single back reference */ + if (safelyCheckNextChar(ptr, patternEnd, '{') && isCountedRepeat(ptr + 2, patternEnd)) { + ptr = readRepeatCounts(ptr + 2, &minRepeats, &maxRepeats, &errorcode); + if (errorcode) + return -1; + if ((minRepeats == 0 && (maxRepeats == 1 || maxRepeats == -1)) || + (minRepeats == 1 && maxRepeats == -1)) + length++; + else + length += 5; + if (safelyCheckNextChar(ptr, patternEnd, '?')) + ptr++; + } + } + continue; + + case '^': /* Single-byte metacharacters */ + case '.': + case '$': + length++; + lastitemlength = 1; + continue; + + case '*': /* These repeats won't be after brackets; */ + case '+': /* those are handled separately */ + case '?': + length++; + goto POSSESSIVE; + + /* This covers the cases of braced repeats after a single char, metachar, + class, or back reference. */ + + case '{': + if (!isCountedRepeat(ptr + 1, patternEnd)) + goto NORMAL_CHAR; + ptr = readRepeatCounts(ptr + 1, &minRepeats, &maxRepeats, &errorcode); + if (errorcode != 0) + return -1; + + /* These special cases just insert one extra opcode */ + + if ((minRepeats == 0 && (maxRepeats == 1 || maxRepeats == -1)) || + (minRepeats == 1 && maxRepeats == -1)) + length++; + + /* These cases might insert additional copies of a preceding character. */ + + else { + if (minRepeats != 1) { + length -= lastitemlength; /* Uncount the original char or metachar */ + if (minRepeats > 0) + length += 3 + lastitemlength; + } + length += lastitemlength + ((maxRepeats > 0) ? 3 : 1); + } + + if (safelyCheckNextChar(ptr, patternEnd, '?')) + ptr++; /* Needs no extra length */ + + POSSESSIVE: /* Test for possessive quantifier */ + if (safelyCheckNextChar(ptr, patternEnd, '+')) { + ptr++; + length += 2 + 2 * LINK_SIZE; /* Allow for atomic brackets */ + } + continue; + + /* An alternation contains an offset to the next branch or ket. If any ims + options changed in the previous branch(es), and/or if we are in a + lookbehind assertion, extra space will be needed at the start of the + branch. This is handled by branch_extra. */ + + case '|': + if (brastackptr == 0) + cd.needOuterBracket = true; + length += 1 + LINK_SIZE + branch_extra; + continue; + + /* A character class uses 33 characters provided that all the character + values are less than 256. Otherwise, it uses a bit map for low valued + characters, and individual items for others. Don't worry about character + types that aren't allowed in classes - they'll get picked up during the + compile. A character class that contains only one single-byte character + uses 2 or 3 bytes, depending on whether it is negated or not. Notice this + where we can. (In UTF-8 mode we can do this only for chars < 128.) */ + + case '[': { + int class_optcount; + if (*(++ptr) == '^') { + class_optcount = 10; /* Greater than one */ + ptr++; + } + else + class_optcount = 0; + + bool class_utf8 = false; + + for (; ptr < patternEnd && *ptr != ']'; ++ptr) { + /* Check for escapes */ + + if (*ptr == '\\') { + c = checkEscape(&ptr, patternEnd, &errorcode, cd.numCapturingBrackets, true); + if (errorcode != 0) + return -1; + + /* Handle escapes that turn into characters */ + + if (c >= 0) + goto NON_SPECIAL_CHARACTER; + + /* Escapes that are meta-things. The normal ones just affect the + bit map, but Unicode properties require an XCLASS extended item. */ + + else + class_optcount = 10; /* \d, \s etc; make sure > 1 */ + } + + /* Anything else increments the possible optimization count. We have to + detect ranges here so that we can compute the number of extra ranges for + caseless wide characters when UCP support is available. If there are wide + characters, we are going to have to use an XCLASS, even for single + characters. */ + + else { + c = *ptr; + + /* Come here from handling \ above when it escapes to a char value */ + + NON_SPECIAL_CHARACTER: + class_optcount++; + + int d = -1; + if (safelyCheckNextChar(ptr, patternEnd, '-')) { + UChar const *hyptr = ptr++; + if (safelyCheckNextChar(ptr, patternEnd, '\\')) { + ptr++; + d = checkEscape(&ptr, patternEnd, &errorcode, cd.numCapturingBrackets, true); + if (errorcode != 0) + return -1; + } + else if ((ptr + 1 < patternEnd) && ptr[1] != ']') + d = *++ptr; + if (d < 0) + ptr = hyptr; /* go back to hyphen as data */ + } + + /* If d >= 0 we have a range. In UTF-8 mode, if the end is > 255, or > + 127 for caseless matching, we will need to use an XCLASS. */ + + if (d >= 0) { + class_optcount = 10; /* Ensure > 1 */ + if (d < c) { + errorcode = ERR8; + return -1; + } + + if ((d > 255 || (ignoreCase && d > 127))) { + unsigned char buffer[6]; + if (!class_utf8) /* Allow for XCLASS overhead */ + { + class_utf8 = true; + length += LINK_SIZE + 2; + } + + /* If we have UCP support, find out how many extra ranges are + needed to map the other case of characters within this range. We + have to mimic the range optimization here, because extending the + range upwards might push d over a boundary that makes it use + another byte in the UTF-8 representation. */ + + if (ignoreCase) { + int occ, ocd; + int cc = c; + int origd = d; + while (getOthercaseRange(&cc, origd, &occ, &ocd)) { + if (occ >= c && ocd <= d) + continue; /* Skip embedded */ + + if (occ < c && ocd >= c - 1) /* Extend the basic range */ + { /* if there is overlap, */ + c = occ; /* noting that if occ < c */ + continue; /* we can't have ocd > d */ + } /* because a subrange is */ + if (ocd > d && occ <= d + 1) /* always shorter than */ + { /* the basic range. */ + d = ocd; + continue; + } + + /* An extra item is needed */ + + length += 1 + encodeUTF8(occ, buffer) + + ((occ == ocd) ? 0 : encodeUTF8(ocd, buffer)); + } + } + + /* The length of the (possibly extended) range */ + + length += 1 + encodeUTF8(c, buffer) + encodeUTF8(d, buffer); + } + + } + + /* We have a single character. There is nothing to be done unless we + are in UTF-8 mode. If the char is > 255, or 127 when caseless, we must + allow for an XCL_SINGLE item, doubled for caselessness if there is UCP + support. */ + + else { + if ((c > 255 || (ignoreCase && c > 127))) { + unsigned char buffer[6]; + class_optcount = 10; /* Ensure > 1 */ + if (!class_utf8) /* Allow for XCLASS overhead */ + { + class_utf8 = true; + length += LINK_SIZE + 2; + } + length += (ignoreCase ? 2 : 1) * (1 + encodeUTF8(c, buffer)); + } + } + } + } + + if (ptr >= patternEnd) { /* Missing terminating ']' */ + errorcode = ERR6; + return -1; + } + + /* We can optimize when there was only one optimizable character. + Note that this does not detect the case of a negated single character. + In that case we do an incorrect length computation, but it's not a serious + problem because the computed length is too large rather than too small. */ + + if (class_optcount == 1) + goto NORMAL_CHAR; + + /* Here, we handle repeats for the class opcodes. */ + { + length += 33; + + /* A repeat needs either 1 or 5 bytes. If it is a possessive quantifier, + we also need extra for wrapping the whole thing in a sub-pattern. */ + + if (safelyCheckNextChar(ptr, patternEnd, '{') && isCountedRepeat(ptr + 2, patternEnd)) { + ptr = readRepeatCounts(ptr + 2, &minRepeats, &maxRepeats, &errorcode); + if (errorcode != 0) + return -1; + if ((minRepeats == 0 && (maxRepeats == 1 || maxRepeats == -1)) || + (minRepeats == 1 && maxRepeats == -1)) + length++; + else + length += 5; + if (safelyCheckNextChar(ptr, patternEnd, '+')) { + ptr++; + length += 2 + 2 * LINK_SIZE; + } else if (safelyCheckNextChar(ptr, patternEnd, '?')) + ptr++; + } + } + continue; + } + + /* Brackets may be genuine groups or special things */ + + case '(': { + int branch_newextra = 0; + int bracket_length = 1 + LINK_SIZE; + bool capturing = false; + + /* Handle special forms of bracket, which all start (? */ + + if (safelyCheckNextChar(ptr, patternEnd, '?')) { + switch (c = (ptr + 2 < patternEnd ? ptr[2] : 0)) { + /* Non-referencing groups and lookaheads just move the pointer on, and + then behave like a non-special bracket, except that they don't increment + the count of extracting brackets. Ditto for the "once only" bracket, + which is in Perl from version 5.005. */ + + case ':': + case '=': + case '!': + ptr += 2; + break; + + /* Else loop checking valid options until ) is met. Anything else is an + error. If we are without any brackets, i.e. at top level, the settings + act as if specified in the options, so massage the options immediately. + This is for backward compatibility with Perl 5.004. */ + + default: + errorcode = ERR12; + return -1; + } + } else + capturing = 1; + + /* Capturing brackets must be counted so we can process escapes in a + Perlish way. If the number exceeds EXTRACT_BASIC_MAX we are going to need + an additional 3 bytes of memory per capturing bracket. */ + + if (capturing) { + bracount++; + if (bracount > EXTRACT_BASIC_MAX) + bracket_length += 3; + } + + /* Save length for computing whole length at end if there's a repeat that + requires duplication of the group. Also save the current value of + branch_extra, and start the new group with the new value. If non-zero, this + will either be 2 for a (?imsx: group, or 3 for a lookbehind assertion. */ + + if (brastackptr >= sizeof(brastack)/sizeof(int)) { + errorcode = ERR17; + return -1; + } + + bralenstack[brastackptr] = branch_extra; + branch_extra = branch_newextra; + + brastack[brastackptr++] = length; + length += bracket_length; + continue; + } + + /* Handle ket. Look for subsequent maxRepeats/minRepeats; for certain sets of values we + have to replicate this bracket up to that many times. If brastackptr is + 0 this is an unmatched bracket which will generate an error, but take care + not to try to access brastack[-1] when computing the length and restoring + the branch_extra value. */ + + case ')': { + int duplength; + length += 1 + LINK_SIZE; + if (brastackptr > 0) { + duplength = length - brastack[--brastackptr]; + branch_extra = bralenstack[brastackptr]; + } + else + duplength = 0; + + /* Leave ptr at the final char; for readRepeatCounts this happens + automatically; for the others we need an increment. */ + + if ((ptr + 1 < patternEnd) && (c = ptr[1]) == '{' && isCountedRepeat(ptr + 2, patternEnd)) { + ptr = readRepeatCounts(ptr + 2, &minRepeats, &maxRepeats, &errorcode); + if (errorcode) + return -1; + } else if (c == '*') { + minRepeats = 0; + maxRepeats = -1; + ptr++; + } else if (c == '+') { + minRepeats = 1; + maxRepeats = -1; + ptr++; + } else if (c == '?') { + minRepeats = 0; + maxRepeats = 1; + ptr++; + } else { + minRepeats = 1; + maxRepeats = 1; + } + + /* If the minimum is zero, we have to allow for an OP_BRAZERO before the + group, and if the maximum is greater than zero, we have to replicate + maxval-1 times; each replication acquires an OP_BRAZERO plus a nesting + bracket set. */ + + int repeatsLength; + if (minRepeats == 0) { + length++; + if (maxRepeats > 0) { + repeatsLength = multiplyWithOverflowCheck(maxRepeats - 1, duplength + 3 + 2 * LINK_SIZE); + if (repeatsLength < 0) { + errorcode = ERR16; + return -1; + } + length += repeatsLength; + if (length > MAX_PATTERN_SIZE) { + errorcode = ERR16; + return -1; + } + } + } + + /* When the minimum is greater than zero, we have to replicate up to + minval-1 times, with no additions required in the copies. Then, if there + is a limited maximum we have to replicate up to maxval-1 times allowing + for a BRAZERO item before each optional copy and nesting brackets for all + but one of the optional copies. */ + + else { + repeatsLength = multiplyWithOverflowCheck(minRepeats - 1, duplength); + if (repeatsLength < 0) { + errorcode = ERR16; + return -1; + } + length += repeatsLength; + if (maxRepeats > minRepeats) { /* Need this test as maxRepeats=-1 means no limit */ + repeatsLength = multiplyWithOverflowCheck(maxRepeats - minRepeats, duplength + 3 + 2 * LINK_SIZE); + if (repeatsLength < 0) { + errorcode = ERR16; + return -1; + } + length += repeatsLength - (2 + 2 * LINK_SIZE); + } + if (length > MAX_PATTERN_SIZE) { + errorcode = ERR16; + return -1; + } + } + + /* Allow space for once brackets for "possessive quantifier" */ + + if (safelyCheckNextChar(ptr, patternEnd, '+')) { + ptr++; + length += 2 + 2 * LINK_SIZE; + } + continue; + } + + /* Non-special character. It won't be space or # in extended mode, so it is + always a genuine character. If we are in a \Q...\E sequence, check for the + end; if not, we have a literal. */ + + default: + NORMAL_CHAR: + length += 2; /* For a one-byte character */ + lastitemlength = 1; /* Default length of last item for repeats */ + + if (c > 127) { + int i; + for (i = 0; i < kjs_pcre_utf8_table1_size; i++) + if (c <= kjs_pcre_utf8_table1[i]) + break; + length += i; + lastitemlength += i; + } + + continue; + } + } + + length += 2 + LINK_SIZE; /* For final KET and END */ + + cd.numCapturingBrackets = bracount; + return length; +} + +/************************************************* +* Compile a Regular Expression * +*************************************************/ + +/* This function takes a string and returns a pointer to a block of store +holding a compiled version of the expression. The original API for this +function had no error code return variable; it is retained for backwards +compatibility. The new function is given a new name. + +Arguments: + pattern the regular expression + options various option bits + errorcodeptr pointer to error code variable (pcre_compile2() only) + can be NULL if you don't want a code value + errorptr pointer to pointer to error text + erroroffset ptr offset in pattern where error was detected + tables pointer to character tables or NULL + +Returns: pointer to compiled data block, or NULL on error, + with errorptr and erroroffset set +*/ + +static inline JSRegExp* returnError(ErrorCode errorcode, const char** errorptr) +{ + *errorptr = errorText(errorcode); + return 0; +} + +JSRegExp* jsRegExpCompile(const UChar* pattern, int patternLength, + JSRegExpIgnoreCaseOption ignoreCase, JSRegExpMultilineOption multiline, + unsigned* numSubpatterns, const char** errorptr, + malloc_t* allocate_function, free_t* free_function) +{ + /* We can't pass back an error message if errorptr is NULL; I guess the best we + can do is just return NULL, but we can set a code value if there is a code pointer. */ + if (!errorptr) + return 0; + *errorptr = NULL; + + CompileData cd; + + ErrorCode errorcode = ERR0; + /* Call this once just to count the brackets. */ + calculateCompiledPatternLength(pattern, patternLength, ignoreCase, cd, errorcode); + /* Call it again to compute the length. */ + int length = calculateCompiledPatternLength(pattern, patternLength, ignoreCase, cd, errorcode); + if (errorcode) + return returnError(errorcode, errorptr); + + if (length > MAX_PATTERN_SIZE) + return returnError(ERR16, errorptr); + + size_t size = length + sizeof(JSRegExp); + JSRegExp* re = reinterpret_cast((*allocate_function)(size)); + + if (!re) + return returnError(ERR13, errorptr); + + re->options = (ignoreCase ? IgnoreCaseOption : 0) | (multiline ? MatchAcrossMultipleLinesOption : 0); + + /* The starting points of the name/number translation table and of the code are + passed around in the compile data block. */ + + unsigned char* codeStart = reinterpret_cast(re + 1); + + /* Set up a starting, non-extracting bracket, then compile the expression. On + error, errorcode will be set non-zero, so we don't need to look at the result + of the function here. */ + + const UChar* ptr = reinterpret_cast(pattern); + const UChar* patternEnd = pattern + patternLength; + unsigned char* code = reinterpret_cast(codeStart); + int firstbyte, reqbyte; + int bracketCount = 0; + if (!cd.needOuterBracket) + compileBranch(re->options, &bracketCount, &code, &ptr, patternEnd, &errorcode, &firstbyte, &reqbyte, cd); + else { + *code = OP_BRA; + compileBracket(re->options, &bracketCount, &code, &ptr, patternEnd, &errorcode, 0, &firstbyte, &reqbyte, cd); + } + re->top_bracket = bracketCount; + re->top_backref = cd.top_backref; + + /* If not reached end of pattern on success, there's an excess bracket. */ + + if (errorcode == 0 && ptr < patternEnd) + errorcode = ERR10; + + /* Fill in the terminating state and check for disastrous overflow, but + if debugging, leave the test till after things are printed out. */ + + *code++ = OP_END; + + ASSERT(code - codeStart <= length); + if (code - codeStart > length) + errorcode = ERR7; + + /* Give an error if there's back reference to a non-existent capturing + subpattern. */ + + if (re->top_backref > re->top_bracket) + errorcode = ERR15; + + /* Failed to compile, or error while post-processing */ + + if (errorcode != ERR0) { + (*free_function)(reinterpret_cast(re)); + return returnError(errorcode, errorptr); + } + + /* If the anchored option was not passed, set the flag if we can determine that + the pattern is anchored by virtue of ^ characters or \A or anything else (such + as starting with .* when DOTALL is set). + + Otherwise, if we know what the first character has to be, save it, because that + speeds up unanchored matches no end. If not, see if we can set the + UseMultiLineFirstByteOptimizationOption flag. This is helpful for multiline matches when all branches + start with ^. and also when all branches start with .* for non-DOTALL matches. + */ + + if (cd.needOuterBracket ? bracketIsAnchored(codeStart) : branchIsAnchored(codeStart)) + re->options |= IsAnchoredOption; + else { + if (firstbyte < 0) { + firstbyte = (cd.needOuterBracket + ? bracketFindFirstAssertedCharacter(codeStart, false) + : branchFindFirstAssertedCharacter(codeStart, false)) + | ((re->options & IgnoreCaseOption) ? REQ_IGNORE_CASE : 0); + } + if (firstbyte >= 0) { + int ch = firstbyte & 255; + if (ch < 127) { + re->first_byte = ((firstbyte & REQ_IGNORE_CASE) && flipCase(ch) == ch) ? ch : firstbyte; + re->options |= UseFirstByteOptimizationOption; + } + } else { + if (cd.needOuterBracket ? bracketNeedsLineStart(codeStart, 0, cd.backrefMap) : branchNeedsLineStart(codeStart, 0, cd.backrefMap)) + re->options |= UseMultiLineFirstByteOptimizationOption; + } + } + + /* For an anchored pattern, we use the "required byte" only if it follows a + variable length item in the regex. Remove the caseless flag for non-caseable + bytes. */ + + if (reqbyte >= 0 && (!(re->options & IsAnchoredOption) || (reqbyte & REQ_VARY))) { + int ch = reqbyte & 255; + if (ch < 127) { + re->req_byte = ((reqbyte & REQ_IGNORE_CASE) && flipCase(ch) == ch) ? (reqbyte & ~REQ_IGNORE_CASE) : reqbyte; + re->options |= UseRequiredByteOptimizationOption; + } + } + + if (numSubpatterns) + *numSubpatterns = re->top_bracket; + return re; +} + +void jsRegExpFree(JSRegExp* re, free_t* free_function) +{ + (*free_function)(reinterpret_cast(re)); +} + +} } // namespace dart::jscre diff --git a/runtime/third_party/jscre/pcre_exec.cpp b/runtime/third_party/jscre/pcre_exec.cpp new file mode 100644 index 00000000000..ab3ba542f58 --- /dev/null +++ b/runtime/third_party/jscre/pcre_exec.cpp @@ -0,0 +1,2085 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. + Copyright (C) 2007 Eric Seidel + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This module contains jsRegExpExecute(), the externally visible function +that does pattern matching using an NFA algorithm, following the rules from +the JavaScript specification. There are also some supporting functions. */ + +#include "config.h" + +#include "pcre_internal.h" + +#include "ASCIICType.h" + +#include +#include +#include /* for memcpy */ + +#ifdef __GNUC__ +#define USE_COMPUTED_GOTO_FOR_MATCH_RECURSION +//#define USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP +#endif + +/* Avoid warnings on Windows. */ +#undef min +#undef max + +namespace dart { namespace jscre { + +#ifndef USE_COMPUTED_GOTO_FOR_MATCH_RECURSION +typedef int ReturnLocation; +#else +typedef void* ReturnLocation; +#endif + +/* Structure for building a chain of data for holding the values of +the subject pointer at the start of each bracket, used to detect when +an empty string has been matched by a bracket to break infinite loops. */ +struct BracketChainNode { + BracketChainNode* previousBracket; + const UChar* bracketStart; +}; + +struct MatchFrame { + ReturnLocation returnLocation; + struct MatchFrame* previousFrame; + + /* Function arguments that may change */ + struct { + const UChar* subjectPtr; + const unsigned char* instructionPtr; + int offsetTop; + BracketChainNode* bracketChain; + } args; + + + /* PCRE uses "fake" recursion built off of gotos, thus + stack-based local variables are not safe to use. Instead we have to + store local variables on the current MatchFrame. */ + struct { + const unsigned char* data; + const unsigned char* startOfRepeatingBracket; + const UChar* subjectPtrAtStartOfInstruction; // Several instrutions stash away a subjectPtr here for later compare + const unsigned char* instructionPtrAtStartOfOnce; + + int repeatOthercase; + + int ctype; + int fc; + int fi; + int length; + int max; + int number; + int offset; + int saveOffset1; + int saveOffset2; + int saveOffset3; + + BracketChainNode bracketChainNode; + } locals; +}; + +/* Structure for passing "static" information around between the functions +doing traditional NFA matching, so that they are thread-safe. */ + +struct MatchData { + int* offsetVector; /* Offset vector */ + int offsetEnd; /* One past the end */ + int offsetMax; /* The maximum usable for return data */ + bool offsetOverflow; /* Set if too many extractions */ + const UChar* startSubject; /* Start of the subject string */ + const UChar* endSubject; /* End of the subject string */ + const UChar* endMatchPtr; /* Subject position at end match */ + int endOffsetTop; /* Highwater mark at end of match */ + bool multiline; + bool ignoreCase; +}; + +/* The maximum remaining length of subject we are prepared to search for a +req_byte match. */ + +#define REQ_BYTE_MAX 1000 + +/* The below limit restricts the number of "recursive" match calls in order to +avoid spending exponential time on complex regular expressions. */ + +static const unsigned matchLimit = 100000; + +#ifdef DEBUG +/************************************************* +* Debugging function to print chars * +*************************************************/ + +/* Print a sequence of chars in printable format, stopping at the end of the +subject if the requested. + +Arguments: + p points to characters + length number to print + isSubject true if printing from within md.startSubject + md pointer to matching data block, if isSubject is true +*/ + +static void pchars(const UChar* p, int length, bool isSubject, const MatchData& md) +{ + if (isSubject && length > md.endSubject - p) + length = md.endSubject - p; + while (length-- > 0) { + int c; + if (isprint(c = *(p++))) + printf("%c", c); + else if (c < 256) + printf("\\x%02x", c); + else + printf("\\x{%x}", c); + } +} +#endif + +/************************************************* +* Match a back-reference * +*************************************************/ + +/* If a back reference hasn't been set, the length that is passed is greater +than the number of characters left in the string, so the match fails. + +Arguments: + offset index into the offset vector + subjectPtr points into the subject + length length to be matched + md points to match data block + +Returns: true if matched +*/ + +static bool matchRef(int offset, const UChar* subjectPtr, int length, const MatchData& md) +{ + const UChar* p = md.startSubject + md.offsetVector[offset]; + +#ifdef DEBUG + if (subjectPtr >= md.endSubject) + printf("matching subject "); + else { + printf("matching subject "); + pchars(subjectPtr, length, true, md); + } + printf(" against backref "); + pchars(p, length, false, md); + printf("\n"); +#endif + + /* Always fail if not enough characters left */ + + if (length > md.endSubject - subjectPtr) + return false; + + /* Separate the caselesss case for speed */ + + if (md.ignoreCase) { + while (length-- > 0) { + UChar c = *p++; + int othercase = kjs_pcre_ucp_othercase(c); + UChar d = *subjectPtr++; + if (c != d && othercase != d) + return false; + } + } + else { + while (length-- > 0) + if (*p++ != *subjectPtr++) + return false; + } + + return true; +} + +#ifndef USE_COMPUTED_GOTO_FOR_MATCH_RECURSION + +/* Use numbered labels and switch statement at the bottom of the match function. */ + +#define RMATCH_WHERE(num) num +#define RRETURN_LABEL RRETURN_SWITCH + +#else + +/* Use GCC's computed goto extension. */ + +/* For one test case this is more than 40% faster than the switch statement. +We could avoid the use of the num argument entirely by using local labels, +but using it for the GCC case as well as the non-GCC case allows us to share +a bit more code and notice if we use conflicting numbers.*/ + +#define RMATCH_WHERE(num) &&RRETURN_##num +#define RRETURN_LABEL *stack.currentFrame->returnLocation + +#endif + +#define RECURSIVE_MATCH_COMMON(num) \ + goto RECURSE;\ + RRETURN_##num: \ + stack.popCurrentFrame(); + +#define RECURSIVE_MATCH(num, ra, rb) \ + do { \ + stack.pushNewFrame((ra), (rb), RMATCH_WHERE(num)); \ + RECURSIVE_MATCH_COMMON(num) \ + } while (0) + +#define RECURSIVE_MATCH_STARTNG_NEW_GROUP(num, ra, rb) \ + do { \ + stack.pushNewFrame((ra), (rb), RMATCH_WHERE(num)); \ + startNewGroup(stack.currentFrame); \ + RECURSIVE_MATCH_COMMON(num) \ + } while (0) + +#define RRETURN goto RRETURN_LABEL + +#define RRETURN_NO_MATCH do { isMatch = false; RRETURN; } while (0) + +/************************************************* +* Match from current position * +*************************************************/ + +/* On entry instructionPtr points to the first opcode, and subjectPtr to the first character +in the subject string, while substringStart holds the value of subjectPtr at the start of the +last bracketed group - used for breaking infinite loops matching zero-length +strings. This function is called recursively in many circumstances. Whenever it +returns a negative (error) response, the outer match() call must also return the +same response. + +Arguments: + subjectPtr pointer in subject + instructionPtr position in code + offsetTop current top pointer + md pointer to "static" info for the match + +Returns: 1 if matched ) these values are >= 0 + 0 if failed to match ) + a negative error value if aborted by an error condition + (e.g. stopped by repeated call or recursion limit) +*/ + +static const unsigned FRAMES_ON_STACK = 16; + +struct MatchStack { + MatchStack() + : framesEnd(frames + FRAMES_ON_STACK) + , currentFrame(frames) + , size(1) // match() creates accesses the first frame w/o calling pushNewFrame + { + ASSERT((sizeof(frames) / sizeof(frames[0])) == FRAMES_ON_STACK); + } + + MatchFrame frames[FRAMES_ON_STACK]; + MatchFrame* framesEnd; + MatchFrame* currentFrame; + unsigned size; + + inline bool canUseStackBufferForNextFrame() + { + return size < FRAMES_ON_STACK; + } + + inline MatchFrame* allocateNextFrame() + { + if (canUseStackBufferForNextFrame()) + return currentFrame + 1; + return new MatchFrame; + } + + inline void pushNewFrame(const unsigned char* instructionPtr, BracketChainNode* bracketChain, ReturnLocation returnLocation) + { + MatchFrame* newframe = allocateNextFrame(); + newframe->previousFrame = currentFrame; + + newframe->args.subjectPtr = currentFrame->args.subjectPtr; + newframe->args.offsetTop = currentFrame->args.offsetTop; + newframe->args.instructionPtr = instructionPtr; + newframe->args.bracketChain = bracketChain; + newframe->returnLocation = returnLocation; + size++; + + currentFrame = newframe; + } + + inline void popCurrentFrame() + { + MatchFrame* oldFrame = currentFrame; + currentFrame = currentFrame->previousFrame; + if (size > FRAMES_ON_STACK) + delete oldFrame; + size--; + } + + void popAllFrames() + { + while (size) + popCurrentFrame(); + } +}; + +static int matchError(int errorCode, MatchStack& stack) +{ + stack.popAllFrames(); + return errorCode; +} + +/* Get the next UTF-8 character, not advancing the pointer, incrementing length + if there are extra bytes. This is called when we know we are in UTF-8 mode. */ + +static inline void getUTF8CharAndIncrementLength(int& c, const unsigned char* subjectPtr, int& len) +{ + c = *subjectPtr; + if ((c & 0xc0) == 0xc0) { + int gcaa = kjs_pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ + int gcss = 6 * gcaa; + c = (c & kjs_pcre_utf8_table3[gcaa]) << gcss; + for (int gcii = 1; gcii <= gcaa; gcii++) { + gcss -= 6; + c |= (subjectPtr[gcii] & 0x3f) << gcss; + } + len += gcaa; + } +} + +static inline void startNewGroup(MatchFrame* currentFrame) +{ + /* At the start of a bracketed group, add the current subject pointer to the + stack of such pointers, to be re-instated at the end of the group when we hit + the closing ket. When match() is called in other circumstances, we don't add to + this stack. */ + + currentFrame->locals.bracketChainNode.previousBracket = currentFrame->args.bracketChain; + currentFrame->locals.bracketChainNode.bracketStart = currentFrame->args.subjectPtr; + currentFrame->args.bracketChain = ¤tFrame->locals.bracketChainNode; +} + +// FIXME: "minimize" means "not greedy", we should invert the callers to ask for "greedy" to be less confusing +static inline void repeatInformationFromInstructionOffset(short instructionOffset, bool& minimize, int& minimumRepeats, int& maximumRepeats) +{ + // Instruction offsets are based off of OP_CRSTAR, OP_STAR, OP_TYPESTAR, OP_NOTSTAR + static const char minimumRepeatsFromInstructionOffset[] = { 0, 0, 1, 1, 0, 0 }; + static const int maximumRepeatsFromInstructionOffset[] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX, 1, 1 }; + + ASSERT(instructionOffset >= 0); + ASSERT(instructionOffset <= (OP_CRMINQUERY - OP_CRSTAR)); + + minimize = (instructionOffset & 1); // this assumes ordering: Instruction, MinimizeInstruction, Instruction2, MinimizeInstruction2 + minimumRepeats = minimumRepeatsFromInstructionOffset[instructionOffset]; + maximumRepeats = maximumRepeatsFromInstructionOffset[instructionOffset]; +} + +static int match(const UChar* subjectPtr, const unsigned char* instructionPtr, int offsetTop, MatchData& md) +{ + bool isMatch = false; + int min; + bool minimize = false; /* Initialization not really needed, but some compilers think so. */ + unsigned matchCount = 0; + + MatchStack stack; + + /* The opcode jump table. */ +#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP +#define EMIT_JUMP_TABLE_ENTRY(opcode) &&LABEL_OP_##opcode, + static void* opcodeJumpTable[256] = { FOR_EACH_OPCODE(EMIT_JUMP_TABLE_ENTRY) }; +#undef EMIT_JUMP_TABLE_ENTRY +#endif + + /* One-time setup of the opcode jump table. */ +#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP + for (int i = 255; !opcodeJumpTable[i]; i--) + opcodeJumpTable[i] = &&CAPTURING_BRACKET; +#endif + +#ifdef USE_COMPUTED_GOTO_FOR_MATCH_RECURSION + // Shark shows this as a hot line + // Using a static const here makes this line disappear, but makes later access hotter (not sure why) + stack.currentFrame->returnLocation = &&RETURN; +#else + stack.currentFrame->returnLocation = 0; +#endif + stack.currentFrame->args.subjectPtr = subjectPtr; + stack.currentFrame->args.instructionPtr = instructionPtr; + stack.currentFrame->args.offsetTop = offsetTop; + stack.currentFrame->args.bracketChain = 0; + startNewGroup(stack.currentFrame); + + /* This is where control jumps back to to effect "recursion" */ + +RECURSE: + if (++matchCount > matchLimit) + return matchError(JSRegExpErrorHitLimit, stack); + + /* Now start processing the operations. */ + +#ifndef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP + while (true) +#endif + { + +#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP +#define BEGIN_OPCODE(opcode) LABEL_OP_##opcode +#define NEXT_OPCODE goto *opcodeJumpTable[*stack.currentFrame->args.instructionPtr] +#else +#define BEGIN_OPCODE(opcode) case OP_##opcode +#define NEXT_OPCODE continue +#endif + +#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP + NEXT_OPCODE; +#else + switch (*stack.currentFrame->args.instructionPtr) +#endif + { + /* Non-capturing bracket: optimized */ + + BEGIN_OPCODE(BRA): + NON_CAPTURING_BRACKET: + DPRINTF(("start bracket 0\n")); + do { + RECURSIVE_MATCH_STARTNG_NEW_GROUP(2, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); + } while (*stack.currentFrame->args.instructionPtr == OP_ALT); + DPRINTF(("bracket 0 failed\n")); + RRETURN; + + /* Skip over large extraction number data if encountered. */ + + BEGIN_OPCODE(BRANUMBER): + stack.currentFrame->args.instructionPtr += 3; + NEXT_OPCODE; + + /* End of the pattern. */ + + BEGIN_OPCODE(END): + md.endMatchPtr = stack.currentFrame->args.subjectPtr; /* Record where we ended */ + md.endOffsetTop = stack.currentFrame->args.offsetTop; /* and how many extracts were taken */ + isMatch = true; + RRETURN; + + /* Assertion brackets. Check the alternative branches in turn - the + matching won't pass the KET for an assertion. If any one branch matches, + the assertion is true. Lookbehind assertions have an OP_REVERSE item at the + start of each branch to move the current point backwards, so the code at + this level is identical to the lookahead case. */ + + BEGIN_OPCODE(ASSERT): + do { + RECURSIVE_MATCH_STARTNG_NEW_GROUP(6, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, NULL); + if (isMatch) + break; + stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); + } while (*stack.currentFrame->args.instructionPtr == OP_ALT); + if (*stack.currentFrame->args.instructionPtr == OP_KET) + RRETURN_NO_MATCH; + + /* Continue from after the assertion, updating the offsets high water + mark, since extracts may have been taken during the assertion. */ + + advanceToEndOfBracket(stack.currentFrame->args.instructionPtr); + stack.currentFrame->args.instructionPtr += 1 + LINK_SIZE; + stack.currentFrame->args.offsetTop = md.endOffsetTop; + NEXT_OPCODE; + + /* Negative assertion: all branches must fail to match */ + + BEGIN_OPCODE(ASSERT_NOT): + do { + RECURSIVE_MATCH_STARTNG_NEW_GROUP(7, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, NULL); + if (isMatch) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); + } while (*stack.currentFrame->args.instructionPtr == OP_ALT); + + stack.currentFrame->args.instructionPtr += 1 + LINK_SIZE; + NEXT_OPCODE; + + /* An alternation is the end of a branch; scan along to find the end of the + bracketed group and go to there. */ + + BEGIN_OPCODE(ALT): + advanceToEndOfBracket(stack.currentFrame->args.instructionPtr); + NEXT_OPCODE; + + /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating + that it may occur zero times. It may repeat infinitely, or not at all - + i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper + repeat limits are compiled as a number of copies, with the optional ones + preceded by BRAZERO or BRAMINZERO. */ + + BEGIN_OPCODE(BRAZERO): { + stack.currentFrame->locals.startOfRepeatingBracket = stack.currentFrame->args.instructionPtr + 1; + RECURSIVE_MATCH_STARTNG_NEW_GROUP(14, stack.currentFrame->locals.startOfRepeatingBracket, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + advanceToEndOfBracket(stack.currentFrame->locals.startOfRepeatingBracket); + stack.currentFrame->args.instructionPtr = stack.currentFrame->locals.startOfRepeatingBracket + 1 + LINK_SIZE; + NEXT_OPCODE; + } + + BEGIN_OPCODE(BRAMINZERO): { + stack.currentFrame->locals.startOfRepeatingBracket = stack.currentFrame->args.instructionPtr + 1; + advanceToEndOfBracket(stack.currentFrame->locals.startOfRepeatingBracket); + RECURSIVE_MATCH_STARTNG_NEW_GROUP(15, stack.currentFrame->locals.startOfRepeatingBracket + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + } + + /* End of a group, repeated or non-repeating. If we are at the end of + an assertion "group", stop matching and return 1, but record the + current high water mark for use by positive assertions. Do this also + for the "once" (not-backup up) groups. */ + + BEGIN_OPCODE(KET): + BEGIN_OPCODE(KETRMIN): + BEGIN_OPCODE(KETRMAX): + stack.currentFrame->locals.instructionPtrAtStartOfOnce = stack.currentFrame->args.instructionPtr - getLinkValue(stack.currentFrame->args.instructionPtr + 1); + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.bracketChain->bracketStart; + + /* Back up the stack of bracket start pointers. */ + + stack.currentFrame->args.bracketChain = stack.currentFrame->args.bracketChain->previousBracket; + + if (*stack.currentFrame->locals.instructionPtrAtStartOfOnce == OP_ASSERT || *stack.currentFrame->locals.instructionPtrAtStartOfOnce == OP_ASSERT_NOT) { + md.endOffsetTop = stack.currentFrame->args.offsetTop; + isMatch = true; + RRETURN; + } + + /* In all other cases except a conditional group we have to check the + group number back at the start and if necessary complete handling an + extraction by setting the offsets and bumping the high water mark. */ + + stack.currentFrame->locals.number = *stack.currentFrame->locals.instructionPtrAtStartOfOnce - OP_BRA; + + /* For extended extraction brackets (large number), we have to fish out + the number from a dummy opcode at the start. */ + + if (stack.currentFrame->locals.number > EXTRACT_BASIC_MAX) + stack.currentFrame->locals.number = get2ByteValue(stack.currentFrame->locals.instructionPtrAtStartOfOnce + 2 + LINK_SIZE); + stack.currentFrame->locals.offset = stack.currentFrame->locals.number << 1; + +#ifdef DEBUG + printf("end bracket %d", stack.currentFrame->locals.number); + printf("\n"); +#endif + + /* Test for a numbered group. This includes groups called as a result + of recursion. Note that whole-pattern recursion is coded as a recurse + into group 0, so it won't be picked up here. Instead, we catch it when + the OP_END is reached. */ + + if (stack.currentFrame->locals.number > 0) { + if (stack.currentFrame->locals.offset >= md.offsetMax) + md.offsetOverflow = true; + else { + md.offsetVector[stack.currentFrame->locals.offset] = + md.offsetVector[md.offsetEnd - stack.currentFrame->locals.number]; + md.offsetVector[stack.currentFrame->locals.offset+1] = stack.currentFrame->args.subjectPtr - md.startSubject; + if (stack.currentFrame->args.offsetTop <= stack.currentFrame->locals.offset) + stack.currentFrame->args.offsetTop = stack.currentFrame->locals.offset + 2; + } + } + + /* For a non-repeating ket, just continue at this level. This also + happens for a repeating ket if no characters were matched in the group. + This is the forcible breaking of infinite loops as implemented in Perl + 5.005. If there is an options reset, it will get obeyed in the normal + course of events. */ + + if (*stack.currentFrame->args.instructionPtr == OP_KET || stack.currentFrame->args.subjectPtr == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) { + stack.currentFrame->args.instructionPtr += 1 + LINK_SIZE; + NEXT_OPCODE; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. */ + + if (*stack.currentFrame->args.instructionPtr == OP_KETRMIN) { + RECURSIVE_MATCH(16, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + RECURSIVE_MATCH_STARTNG_NEW_GROUP(17, stack.currentFrame->locals.instructionPtrAtStartOfOnce, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + } else { /* OP_KETRMAX */ + RECURSIVE_MATCH_STARTNG_NEW_GROUP(18, stack.currentFrame->locals.instructionPtrAtStartOfOnce, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + RECURSIVE_MATCH(19, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + } + RRETURN; + + /* Start of subject. */ + + BEGIN_OPCODE(CIRC): + if (stack.currentFrame->args.subjectPtr != md.startSubject) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + /* After internal newline if multiline. */ + + BEGIN_OPCODE(BOL): + if (stack.currentFrame->args.subjectPtr != md.startSubject && !isNewline(stack.currentFrame->args.subjectPtr[-1])) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + /* End of subject. */ + + BEGIN_OPCODE(DOLL): + if (stack.currentFrame->args.subjectPtr < md.endSubject) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + /* Before internal newline if multiline. */ + + BEGIN_OPCODE(EOL): + if (stack.currentFrame->args.subjectPtr < md.endSubject && !isNewline(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + /* Word boundary assertions */ + + BEGIN_OPCODE(NOT_WORD_BOUNDARY): + BEGIN_OPCODE(WORD_BOUNDARY): { + bool currentCharIsWordChar = false; + bool previousCharIsWordChar = false; + + if (stack.currentFrame->args.subjectPtr > md.startSubject) + previousCharIsWordChar = isWordChar(stack.currentFrame->args.subjectPtr[-1]); + if (stack.currentFrame->args.subjectPtr < md.endSubject) + currentCharIsWordChar = isWordChar(*stack.currentFrame->args.subjectPtr); + + /* Now see if the situation is what we want */ + bool wordBoundaryDesired = (*stack.currentFrame->args.instructionPtr++ == OP_WORD_BOUNDARY); + if (wordBoundaryDesired ? currentCharIsWordChar == previousCharIsWordChar : currentCharIsWordChar != previousCharIsWordChar) + RRETURN_NO_MATCH; + NEXT_OPCODE; + } + + /* Match a single character type; inline for speed */ + + BEGIN_OPCODE(NOT_NEWLINE): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (isNewline(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + BEGIN_OPCODE(NOT_DIGIT): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (isASCIIDigit(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + BEGIN_OPCODE(DIGIT): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (!isASCIIDigit(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + BEGIN_OPCODE(NOT_WHITESPACE): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (isSpaceChar(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + BEGIN_OPCODE(WHITESPACE): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (!isSpaceChar(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + BEGIN_OPCODE(NOT_WORDCHAR): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (isWordChar(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + BEGIN_OPCODE(WORDCHAR): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (!isWordChar(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + /* Match a back reference, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. The code is similar + to that for character classes, but repeated for efficiency. Then obey + similar code to character type repeats - written out again for speed. + However, if the referenced string is the empty string, always treat + it as matched, any number of times (otherwise there could be infinite + loops). */ + + BEGIN_OPCODE(REF): + stack.currentFrame->locals.offset = get2ByteValue(stack.currentFrame->args.instructionPtr + 1) << 1; /* Doubled ref number */ + stack.currentFrame->args.instructionPtr += 3; /* Advance past item */ + + /* If the reference is unset, set the length to be longer than the amount + of subject left; this ensures that every attempt at a match fails. We + can't just fail here, because of the possibility of quantifiers with zero + minima. */ + + if (stack.currentFrame->locals.offset >= stack.currentFrame->args.offsetTop || md.offsetVector[stack.currentFrame->locals.offset] < 0) + stack.currentFrame->locals.length = 0; + else + stack.currentFrame->locals.length = md.offsetVector[stack.currentFrame->locals.offset+1] - md.offsetVector[stack.currentFrame->locals.offset]; + + /* Set up for repetition, or handle the non-repeated case */ + + switch (*stack.currentFrame->args.instructionPtr) { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_CRSTAR, minimize, min, stack.currentFrame->locals.max); + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*stack.currentFrame->args.instructionPtr == OP_CRMINRANGE); + min = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 3); + if (stack.currentFrame->locals.max == 0) + stack.currentFrame->locals.max = INT_MAX; + stack.currentFrame->args.instructionPtr += 5; + break; + + default: /* No repeat follows */ + if (!matchRef(stack.currentFrame->locals.offset, stack.currentFrame->args.subjectPtr, stack.currentFrame->locals.length, md)) + RRETURN_NO_MATCH; + stack.currentFrame->args.subjectPtr += stack.currentFrame->locals.length; + NEXT_OPCODE; + } + + /* If the length of the reference is zero, just continue with the + main loop. */ + + if (stack.currentFrame->locals.length == 0) + NEXT_OPCODE; + + /* First, ensure the minimum number of matches are present. */ + + for (int i = 1; i <= min; i++) { + if (!matchRef(stack.currentFrame->locals.offset, stack.currentFrame->args.subjectPtr, stack.currentFrame->locals.length, md)) + RRETURN_NO_MATCH; + stack.currentFrame->args.subjectPtr += stack.currentFrame->locals.length; + } + + /* If min = max, continue at the same level without recursion. + They are not both allowed to be zero. */ + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + /* If minimizing, keep trying and advancing the pointer */ + + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(20, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || !matchRef(stack.currentFrame->locals.offset, stack.currentFrame->args.subjectPtr, stack.currentFrame->locals.length, md)) + RRETURN; + stack.currentFrame->args.subjectPtr += stack.currentFrame->locals.length; + } + /* Control never reaches here */ + } + + /* If maximizing, find the longest string and work backwards */ + + else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (!matchRef(stack.currentFrame->locals.offset, stack.currentFrame->args.subjectPtr, stack.currentFrame->locals.length, md)) + break; + stack.currentFrame->args.subjectPtr += stack.currentFrame->locals.length; + } + while (stack.currentFrame->args.subjectPtr >= stack.currentFrame->locals.subjectPtrAtStartOfInstruction) { + RECURSIVE_MATCH(21, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + stack.currentFrame->args.subjectPtr -= stack.currentFrame->locals.length; + } + RRETURN_NO_MATCH; + } + /* Control never reaches here */ + + /* Match a bit-mapped character class, possibly repeatedly. This op code is + used when all the characters in the class have values in the range 0-255, + and either the matching is caseful, or the characters are in the range + 0-127 when UTF-8 processing is enabled. The only difference between + OP_CLASS and OP_NCLASS occurs when a data character outside the range is + encountered. + + First, look past the end of the item to see if there is repeat information + following. Then obey similar code to character type repeats - written out + again for speed. */ + + BEGIN_OPCODE(NCLASS): + BEGIN_OPCODE(CLASS): + stack.currentFrame->locals.data = stack.currentFrame->args.instructionPtr + 1; /* Save for matching */ + stack.currentFrame->args.instructionPtr += 33; /* Advance past the item */ + + switch (*stack.currentFrame->args.instructionPtr) { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_CRSTAR, minimize, min, stack.currentFrame->locals.max); + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*stack.currentFrame->args.instructionPtr == OP_CRMINRANGE); + min = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 3); + if (stack.currentFrame->locals.max == 0) + stack.currentFrame->locals.max = INT_MAX; + stack.currentFrame->args.instructionPtr += 5; + break; + + default: /* No repeat follows */ + min = stack.currentFrame->locals.max = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + + for (int i = 1; i <= min; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + int c = *stack.currentFrame->args.subjectPtr++; + if (c > 255) { + if (stack.currentFrame->locals.data[-1] == OP_CLASS) + RRETURN_NO_MATCH; + } else { + if (!(stack.currentFrame->locals.data[c / 8] & (1 << (c & 7)))) + RRETURN_NO_MATCH; + } + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(22, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN; + int c = *stack.currentFrame->args.subjectPtr++; + if (c > 255) { + if (stack.currentFrame->locals.data[-1] == OP_CLASS) + RRETURN; + } else { + if ((stack.currentFrame->locals.data[c/8] & (1 << (c&7))) == 0) + RRETURN; + } + } + /* Control never reaches here */ + } + /* If maximizing, find the longest possible run, then work backwards. */ + else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (c > 255) { + if (stack.currentFrame->locals.data[-1] == OP_CLASS) + break; + } else { + if (!(stack.currentFrame->locals.data[c / 8] & (1 << (c & 7)))) + break; + } + ++stack.currentFrame->args.subjectPtr; + } + for (;;) { + RECURSIVE_MATCH(24, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) + break; /* Stop if tried at original pos */ + } + + RRETURN; + } + /* Control never reaches here */ + + /* Match an extended character class. */ + + BEGIN_OPCODE(XCLASS): + stack.currentFrame->locals.data = stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE; /* Save for matching */ + stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); /* Advance past the item */ + + switch (*stack.currentFrame->args.instructionPtr) { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_CRSTAR, minimize, min, stack.currentFrame->locals.max); + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*stack.currentFrame->args.instructionPtr == OP_CRMINRANGE); + min = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 3); + if (stack.currentFrame->locals.max == 0) + stack.currentFrame->locals.max = INT_MAX; + stack.currentFrame->args.instructionPtr += 5; + break; + + default: /* No repeat follows */ + min = stack.currentFrame->locals.max = 1; + } + + /* First, ensure the minimum number of matches are present. */ + + for (int i = 1; i <= min; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + int c = *stack.currentFrame->args.subjectPtr++; + if (!kjs_pcre_xclass(c, stack.currentFrame->locals.data)) + RRETURN_NO_MATCH; + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(26, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN; + int c = *stack.currentFrame->args.subjectPtr++; + if (!kjs_pcre_xclass(c, stack.currentFrame->locals.data)) + RRETURN; + } + /* Control never reaches here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (!kjs_pcre_xclass(c, stack.currentFrame->locals.data)) + break; + ++stack.currentFrame->args.subjectPtr; + } + for(;;) { + RECURSIVE_MATCH(27, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) + break; /* Stop if tried at original pos */ + } + RRETURN; + } + + /* Control never reaches here */ + + /* Match a single character, casefully */ + + BEGIN_OPCODE(CHAR): + stack.currentFrame->locals.length = 1; + stack.currentFrame->args.instructionPtr++; + getUTF8CharAndIncrementLength(stack.currentFrame->locals.fc, stack.currentFrame->args.instructionPtr, stack.currentFrame->locals.length); + stack.currentFrame->args.instructionPtr += stack.currentFrame->locals.length; + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (stack.currentFrame->locals.fc != *stack.currentFrame->args.subjectPtr++) + RRETURN_NO_MATCH; + NEXT_OPCODE; + + /* Match a single character, caselessly */ + + BEGIN_OPCODE(CHAR_IGNORING_CASE): { + stack.currentFrame->locals.length = 1; + stack.currentFrame->args.instructionPtr++; + getUTF8CharAndIncrementLength(stack.currentFrame->locals.fc, stack.currentFrame->args.instructionPtr, stack.currentFrame->locals.length); + stack.currentFrame->args.instructionPtr += stack.currentFrame->locals.length; + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + int dc = *stack.currentFrame->args.subjectPtr++; + if (stack.currentFrame->locals.fc != dc && kjs_pcre_ucp_othercase(stack.currentFrame->locals.fc) != dc) + RRETURN_NO_MATCH; + NEXT_OPCODE; + } + + /* Match a single ASCII character. */ + + BEGIN_OPCODE(ASCII_CHAR): + if (md.endSubject == stack.currentFrame->args.subjectPtr) + RRETURN_NO_MATCH; + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->args.instructionPtr[1]) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + stack.currentFrame->args.instructionPtr += 2; + NEXT_OPCODE; + + /* Match one of two cases of an ASCII letter. */ + + BEGIN_OPCODE(ASCII_LETTER_IGNORING_CASE): + if (md.endSubject == stack.currentFrame->args.subjectPtr) + RRETURN_NO_MATCH; + if ((*stack.currentFrame->args.subjectPtr | 0x20) != stack.currentFrame->args.instructionPtr[1]) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + stack.currentFrame->args.instructionPtr += 2; + NEXT_OPCODE; + + /* Match a single character repeatedly; different opcodes share code. */ + + BEGIN_OPCODE(EXACT): + min = stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + minimize = false; + stack.currentFrame->args.instructionPtr += 3; + goto REPEATCHAR; + + BEGIN_OPCODE(UPTO): + BEGIN_OPCODE(MINUPTO): + min = 0; + stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + minimize = *stack.currentFrame->args.instructionPtr == OP_MINUPTO; + stack.currentFrame->args.instructionPtr += 3; + goto REPEATCHAR; + + BEGIN_OPCODE(STAR): + BEGIN_OPCODE(MINSTAR): + BEGIN_OPCODE(PLUS): + BEGIN_OPCODE(MINPLUS): + BEGIN_OPCODE(QUERY): + BEGIN_OPCODE(MINQUERY): + repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_STAR, minimize, min, stack.currentFrame->locals.max); + + /* Common code for all repeated single-character matches. We can give + up quickly if there are fewer than the minimum number of characters left in + the subject. */ + + REPEATCHAR: + + stack.currentFrame->locals.length = 1; + getUTF8CharAndIncrementLength(stack.currentFrame->locals.fc, stack.currentFrame->args.instructionPtr, stack.currentFrame->locals.length); + if (min * (stack.currentFrame->locals.fc > 0xFFFF ? 2 : 1) > md.endSubject - stack.currentFrame->args.subjectPtr) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr += stack.currentFrame->locals.length; + + if (stack.currentFrame->locals.fc <= 0xFFFF) { + int othercase = md.ignoreCase ? kjs_pcre_ucp_othercase(stack.currentFrame->locals.fc) : -1; + + for (int i = 1; i <= min; i++) { + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc && *stack.currentFrame->args.subjectPtr != othercase) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + if (minimize) { + stack.currentFrame->locals.repeatOthercase = othercase; + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(28, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN; + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc && *stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.repeatOthercase) + RRETURN; + ++stack.currentFrame->args.subjectPtr; + } + /* Control never reaches here */ + } else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc && *stack.currentFrame->args.subjectPtr != othercase) + break; + ++stack.currentFrame->args.subjectPtr; + } + while (stack.currentFrame->args.subjectPtr >= stack.currentFrame->locals.subjectPtrAtStartOfInstruction) { + RECURSIVE_MATCH(29, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + --stack.currentFrame->args.subjectPtr; + } + RRETURN_NO_MATCH; + } + /* Control never reaches here */ + } else { + /* No case on surrogate pairs, so no need to bother with "othercase". */ + + for (int i = 1; i <= min; i++) { + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc) + RRETURN_NO_MATCH; + stack.currentFrame->args.subjectPtr += 2; + } + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(30, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN; + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc) + RRETURN; + stack.currentFrame->args.subjectPtr += 2; + } + /* Control never reaches here */ + } else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr > md.endSubject - 2) + break; + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc) + break; + stack.currentFrame->args.subjectPtr += 2; + } + while (stack.currentFrame->args.subjectPtr >= stack.currentFrame->locals.subjectPtrAtStartOfInstruction) { + RECURSIVE_MATCH(31, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + stack.currentFrame->args.subjectPtr -= 2; + } + RRETURN_NO_MATCH; + } + /* Control never reaches here */ + } + /* Control never reaches here */ + + /* Match a negated single one-byte character. */ + + BEGIN_OPCODE(NOT): { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + int c = *stack.currentFrame->args.subjectPtr++; + if (md.ignoreCase) { + if (c < 128) + c = toLowerCase(c); + if (toLowerCase(*stack.currentFrame->args.instructionPtr++) == c) + RRETURN_NO_MATCH; + } else { + if (*stack.currentFrame->args.instructionPtr++ == c) + RRETURN_NO_MATCH; + } + NEXT_OPCODE; + } + + /* Match a negated single one-byte character repeatedly. This is almost a + repeat of the code for a repeated single character, but I haven't found a + nice way of commoning these up that doesn't require a test of the + positive/negative option for each character match. Maybe that wouldn't add + very much to the time taken, but character matching *is* what this is all + about... */ + + BEGIN_OPCODE(NOTEXACT): + min = stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + minimize = false; + stack.currentFrame->args.instructionPtr += 3; + goto REPEATNOTCHAR; + + BEGIN_OPCODE(NOTUPTO): + BEGIN_OPCODE(NOTMINUPTO): + min = 0; + stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + minimize = *stack.currentFrame->args.instructionPtr == OP_NOTMINUPTO; + stack.currentFrame->args.instructionPtr += 3; + goto REPEATNOTCHAR; + + BEGIN_OPCODE(NOTSTAR): + BEGIN_OPCODE(NOTMINSTAR): + BEGIN_OPCODE(NOTPLUS): + BEGIN_OPCODE(NOTMINPLUS): + BEGIN_OPCODE(NOTQUERY): + BEGIN_OPCODE(NOTMINQUERY): + repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_NOTSTAR, minimize, min, stack.currentFrame->locals.max); + + /* Common code for all repeated single-byte matches. We can give up quickly + if there are fewer than the minimum number of bytes left in the + subject. */ + + REPEATNOTCHAR: + if (min > md.endSubject - stack.currentFrame->args.subjectPtr) + RRETURN_NO_MATCH; + stack.currentFrame->locals.fc = *stack.currentFrame->args.instructionPtr++; + + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If min = max, continue at the same + level without recursing. Otherwise, if minimizing, keep trying the rest of + the expression and advancing one matching character if failing, up to the + maximum. Alternatively, if maximizing, find the maximum number of + characters and work backwards. */ + + DPRINTF(("negative matching %c{%d,%d}\n", stack.currentFrame->locals.fc, min, stack.currentFrame->locals.max)); + + if (md.ignoreCase) { + if (stack.currentFrame->locals.fc < 128) + stack.currentFrame->locals.fc = toLowerCase(stack.currentFrame->locals.fc); + + for (int i = 1; i <= min; i++) { + int d = *stack.currentFrame->args.subjectPtr++; + if (d < 128) + d = toLowerCase(d); + if (stack.currentFrame->locals.fc == d) + RRETURN_NO_MATCH; + } + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(38, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + int d = *stack.currentFrame->args.subjectPtr++; + if (d < 128) + d = toLowerCase(d); + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject || stack.currentFrame->locals.fc == d) + RRETURN; + } + /* Control never reaches here */ + } + + /* Maximize case */ + + else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int d = *stack.currentFrame->args.subjectPtr; + if (d < 128) + d = toLowerCase(d); + if (stack.currentFrame->locals.fc == d) + break; + ++stack.currentFrame->args.subjectPtr; + } + for (;;) { + RECURSIVE_MATCH(40, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) + break; /* Stop if tried at original pos */ + } + + RRETURN; + } + /* Control never reaches here */ + } + + /* Caseful comparisons */ + + else { + for (int i = 1; i <= min; i++) { + int d = *stack.currentFrame->args.subjectPtr++; + if (stack.currentFrame->locals.fc == d) + RRETURN_NO_MATCH; + } + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(42, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + int d = *stack.currentFrame->args.subjectPtr++; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject || stack.currentFrame->locals.fc == d) + RRETURN; + } + /* Control never reaches here */ + } + + /* Maximize case */ + + else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int d = *stack.currentFrame->args.subjectPtr; + if (stack.currentFrame->locals.fc == d) + break; + ++stack.currentFrame->args.subjectPtr; + } + for (;;) { + RECURSIVE_MATCH(44, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) + break; /* Stop if tried at original pos */ + } + + RRETURN; + } + } + /* Control never reaches here */ + + /* Match a single character type repeatedly; several different opcodes + share code. This is very similar to the code for single characters, but we + repeat it in the interests of efficiency. */ + + BEGIN_OPCODE(TYPEEXACT): + min = stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + minimize = true; + stack.currentFrame->args.instructionPtr += 3; + goto REPEATTYPE; + + BEGIN_OPCODE(TYPEUPTO): + BEGIN_OPCODE(TYPEMINUPTO): + min = 0; + stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + minimize = *stack.currentFrame->args.instructionPtr == OP_TYPEMINUPTO; + stack.currentFrame->args.instructionPtr += 3; + goto REPEATTYPE; + + BEGIN_OPCODE(TYPESTAR): + BEGIN_OPCODE(TYPEMINSTAR): + BEGIN_OPCODE(TYPEPLUS): + BEGIN_OPCODE(TYPEMINPLUS): + BEGIN_OPCODE(TYPEQUERY): + BEGIN_OPCODE(TYPEMINQUERY): + repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_TYPESTAR, minimize, min, stack.currentFrame->locals.max); + + /* Common code for all repeated single character type matches. Note that + in UTF-8 mode, '.' matches a character of any length, but for the other + character types, the valid characters are all one-byte long. */ + + REPEATTYPE: + stack.currentFrame->locals.ctype = *stack.currentFrame->args.instructionPtr++; /* Code for the character type */ + + /* First, ensure the minimum number of matches are present. Use inline + code for maximizing the speed, and do the type test once at the start + (i.e. keep it out of the loop). Also we can test that there are at least + the minimum number of characters before we start. */ + + if (min > md.endSubject - stack.currentFrame->args.subjectPtr) + RRETURN_NO_MATCH; + if (min > 0) { + switch (stack.currentFrame->locals.ctype) { + case OP_NOT_NEWLINE: + for (int i = 1; i <= min; i++) { + if (isNewline(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_NOT_DIGIT: + for (int i = 1; i <= min; i++) { + if (isASCIIDigit(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_DIGIT: + for (int i = 1; i <= min; i++) { + if (!isASCIIDigit(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_NOT_WHITESPACE: + for (int i = 1; i <= min; i++) { + if (isSpaceChar(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_WHITESPACE: + for (int i = 1; i <= min; i++) { + if (!isSpaceChar(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_NOT_WORDCHAR: + for (int i = 1; i <= min; i++) { + if (isWordChar(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_WORDCHAR: + for (int i = 1; i <= min; i++) { + if (!isWordChar(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + default: + ASSERT_NOT_REACHED(); + return matchError(JSRegExpErrorInternal, stack); + } /* End switch(stack.currentFrame->locals.ctype) */ + } + + /* If min = max, continue at the same level without recursing */ + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + /* If minimizing, we have to test the rest of the pattern before each + subsequent match. */ + + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(48, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN; + + int c = *stack.currentFrame->args.subjectPtr++; + switch (stack.currentFrame->locals.ctype) { + case OP_NOT_NEWLINE: + if (isNewline(c)) + RRETURN; + break; + + case OP_NOT_DIGIT: + if (isASCIIDigit(c)) + RRETURN; + break; + + case OP_DIGIT: + if (!isASCIIDigit(c)) + RRETURN; + break; + + case OP_NOT_WHITESPACE: + if (isSpaceChar(c)) + RRETURN; + break; + + case OP_WHITESPACE: + if (!isSpaceChar(c)) + RRETURN; + break; + + case OP_NOT_WORDCHAR: + if (isWordChar(c)) + RRETURN; + break; + + case OP_WORDCHAR: + if (!isWordChar(c)) + RRETURN; + break; + + default: + ASSERT_NOT_REACHED(); + return matchError(JSRegExpErrorInternal, stack); + } + } + /* Control never reaches here */ + } + + /* If maximizing it is worth using inline code for speed, doing the type + test once at the start (i.e. keep it out of the loop). */ + + else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; /* Remember where we started */ + + switch (stack.currentFrame->locals.ctype) { + case OP_NOT_NEWLINE: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject || isNewline(*stack.currentFrame->args.subjectPtr)) + break; + stack.currentFrame->args.subjectPtr++; + } + break; + + case OP_NOT_DIGIT: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (isASCIIDigit(c)) + break; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_DIGIT: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (!isASCIIDigit(c)) + break; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_NOT_WHITESPACE: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (isSpaceChar(c)) + break; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_WHITESPACE: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (!isSpaceChar(c)) + break; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_NOT_WORDCHAR: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (isWordChar(c)) + break; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_WORDCHAR: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (!isWordChar(c)) + break; + ++stack.currentFrame->args.subjectPtr; + } + break; + + default: + ASSERT_NOT_REACHED(); + return matchError(JSRegExpErrorInternal, stack); + } + + /* stack.currentFrame->args.subjectPtr is now past the end of the maximum run */ + + for (;;) { + RECURSIVE_MATCH(52, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) + break; /* Stop if tried at original pos */ + } + + /* Get here if we can't make it match with any permitted repetitions */ + + RRETURN; + } + /* Control never reaches here */ + + BEGIN_OPCODE(CRMINPLUS): + BEGIN_OPCODE(CRMINQUERY): + BEGIN_OPCODE(CRMINRANGE): + BEGIN_OPCODE(CRMINSTAR): + BEGIN_OPCODE(CRPLUS): + BEGIN_OPCODE(CRQUERY): + BEGIN_OPCODE(CRRANGE): + BEGIN_OPCODE(CRSTAR): + ASSERT_NOT_REACHED(); + return matchError(JSRegExpErrorInternal, stack); + +#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP + CAPTURING_BRACKET: +#else + default: +#endif + /* Opening capturing bracket. If there is space in the offset vector, save + the current subject position in the working slot at the top of the vector. We + mustn't change the current values of the data slot, because they may be set + from a previous iteration of this group, and be referred to by a reference + inside the group. + + If the bracket fails to match, we need to restore this value and also the + values of the final offsets, in case they were set by a previous iteration of + the same bracket. + + If there isn't enough space in the offset vector, treat this as if it were a + non-capturing bracket. Don't worry about setting the flag for the error case + here; that is handled in the code for KET. */ + + ASSERT(*stack.currentFrame->args.instructionPtr > OP_BRA); + + stack.currentFrame->locals.number = *stack.currentFrame->args.instructionPtr - OP_BRA; + + /* For extended extraction brackets (large number), we have to fish out the + number from a dummy opcode at the start. */ + + if (stack.currentFrame->locals.number > EXTRACT_BASIC_MAX) + stack.currentFrame->locals.number = get2ByteValue(stack.currentFrame->args.instructionPtr + 2 + LINK_SIZE); + stack.currentFrame->locals.offset = stack.currentFrame->locals.number << 1; + +#ifdef DEBUG + printf("start bracket %d subject=", stack.currentFrame->locals.number); + pchars(stack.currentFrame->args.subjectPtr, 16, true, md); + printf("\n"); +#endif + + if (stack.currentFrame->locals.offset < md.offsetMax) { + stack.currentFrame->locals.saveOffset1 = md.offsetVector[stack.currentFrame->locals.offset]; + stack.currentFrame->locals.saveOffset2 = md.offsetVector[stack.currentFrame->locals.offset + 1]; + stack.currentFrame->locals.saveOffset3 = md.offsetVector[md.offsetEnd - stack.currentFrame->locals.number]; + + DPRINTF(("saving %d %d %d\n", stack.currentFrame->locals.saveOffset1, stack.currentFrame->locals.saveOffset2, stack.currentFrame->locals.saveOffset3)); + md.offsetVector[md.offsetEnd - stack.currentFrame->locals.number] = stack.currentFrame->args.subjectPtr - md.startSubject; + + do { + RECURSIVE_MATCH_STARTNG_NEW_GROUP(1, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain); + if (isMatch) + RRETURN; + stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); + } while (*stack.currentFrame->args.instructionPtr == OP_ALT); + + DPRINTF(("bracket %d failed\n", stack.currentFrame->locals.number)); + + md.offsetVector[stack.currentFrame->locals.offset] = stack.currentFrame->locals.saveOffset1; + md.offsetVector[stack.currentFrame->locals.offset + 1] = stack.currentFrame->locals.saveOffset2; + md.offsetVector[md.offsetEnd - stack.currentFrame->locals.number] = stack.currentFrame->locals.saveOffset3; + + RRETURN; + } + + /* Insufficient room for saving captured contents */ + + goto NON_CAPTURING_BRACKET; + } + + /* Do not stick any code in here without much thought; it is assumed + that "continue" in the code above comes out to here to repeat the main + loop. */ + + } /* End of main loop */ + + ASSERT_NOT_REACHED(); + +#ifndef USE_COMPUTED_GOTO_FOR_MATCH_RECURSION + +RRETURN_SWITCH: + switch (stack.currentFrame->returnLocation) { + case 0: goto RETURN; + case 1: goto RRETURN_1; + case 2: goto RRETURN_2; + case 6: goto RRETURN_6; + case 7: goto RRETURN_7; + case 14: goto RRETURN_14; + case 15: goto RRETURN_15; + case 16: goto RRETURN_16; + case 17: goto RRETURN_17; + case 18: goto RRETURN_18; + case 19: goto RRETURN_19; + case 20: goto RRETURN_20; + case 21: goto RRETURN_21; + case 22: goto RRETURN_22; + case 24: goto RRETURN_24; + case 26: goto RRETURN_26; + case 27: goto RRETURN_27; + case 28: goto RRETURN_28; + case 29: goto RRETURN_29; + case 30: goto RRETURN_30; + case 31: goto RRETURN_31; + case 38: goto RRETURN_38; + case 40: goto RRETURN_40; + case 42: goto RRETURN_42; + case 44: goto RRETURN_44; + case 48: goto RRETURN_48; + case 52: goto RRETURN_52; + } + + ASSERT_NOT_REACHED(); + return matchError(JSRegExpErrorInternal, stack); + +#endif + +RETURN: + return isMatch; +} + + +/************************************************* +* Execute a Regular Expression * +*************************************************/ + +/* This function applies a compiled re to a subject string and picks out +portions of the string if it matches. Two elements in the vector are set for +each substring: the offsets to the start and end of the substring. + +Arguments: + re points to the compiled expression + extra_data points to extra data or is NULL + subject points to the subject string + length length of subject string (may contain binary zeros) + start_offset where to start in the subject string + options option bits + offsets points to a vector of ints to be filled in with offsets + offsetcount the number of elements in the vector + +Returns: > 0 => success; value is the number of elements filled in + = 0 => success, but offsets is not big enough + -1 => failed to match + < -1 => some kind of unexpected problem +*/ + +static void tryFirstByteOptimization(const UChar*& subjectPtr, const UChar* endSubject, int first_byte, bool first_byte_caseless, bool useMultiLineFirstCharOptimization, const UChar* originalSubjectStart) +{ + // If first_byte is set, try scanning to the first instance of that byte + // no need to try and match against any earlier part of the subject string. + if (first_byte >= 0) { + UChar first_char = first_byte; + if (first_byte_caseless) + while (subjectPtr < endSubject) { + int c = *subjectPtr; + if (c > 127) + break; + if (toLowerCase(c) == first_char) + break; + subjectPtr++; + } + else { + while (subjectPtr < endSubject && *subjectPtr != first_char) + subjectPtr++; + } + } else if (useMultiLineFirstCharOptimization) { + /* Or to just after \n for a multiline match if possible */ + // I'm not sure why this != originalSubjectStart check is necessary -- ecs 11/18/07 + if (subjectPtr > originalSubjectStart) { + while (subjectPtr < endSubject && !isNewline(subjectPtr[-1])) + subjectPtr++; + } + } +} + +static bool tryRequiredByteOptimization(const UChar*& subjectPtr, const UChar* endSubject, int req_byte, int req_byte2, bool req_byte_caseless, bool hasFirstByte, const UChar*& reqBytePtr) +{ + /* If req_byte is set, we know that that character must appear in the subject + for the match to succeed. If the first character is set, req_byte must be + later in the subject; otherwise the test starts at the match point. This + optimization can save a huge amount of backtracking in patterns with nested + unlimited repeats that aren't going to match. Writing separate code for + cased/caseless versions makes it go faster, as does using an autoincrement + and backing off on a match. + + HOWEVER: when the subject string is very, very long, searching to its end can + take a long time, and give bad performance on quite ordinary patterns. This + showed up when somebody was matching /^C/ on a 32-megabyte string... so we + don't do this when the string is sufficiently long. + */ + + if (req_byte >= 0 && endSubject - subjectPtr < REQ_BYTE_MAX) { + const UChar* p = subjectPtr + (hasFirstByte ? 1 : 0); + + /* We don't need to repeat the search if we haven't yet reached the + place we found it at last time. */ + + if (p > reqBytePtr) { + if (req_byte_caseless) { + while (p < endSubject) { + int pp = *p++; + if (pp == req_byte || pp == req_byte2) { + p--; + break; + } + } + } else { + while (p < endSubject) { + if (*p++ == req_byte) { + p--; + break; + } + } + } + + /* If we can't find the required character, break the matching loop */ + + if (p >= endSubject) + return true; + + /* If we have found the required character, save the point where we + found it, so that we don't search again next time round the loop if + the start hasn't passed this character yet. */ + + reqBytePtr = p; + } + } + return false; +} + +int jsRegExpExecute(const JSRegExp* re, + const UChar* subject, int length, int start_offset, int* offsets, + int offsetcount) +{ + ASSERT(re); + ASSERT(subject); + ASSERT(offsetcount >= 0); + ASSERT(offsets || offsetcount == 0); + + MatchData matchBlock; + matchBlock.startSubject = subject; + matchBlock.endSubject = matchBlock.startSubject + length; + const UChar* endSubject = matchBlock.endSubject; + + matchBlock.multiline = (re->options & MatchAcrossMultipleLinesOption); + matchBlock.ignoreCase = (re->options & IgnoreCaseOption); + + /* If the expression has got more back references than the offsets supplied can + hold, we get a temporary chunk of working store to use during the matching. + Otherwise, we can use the vector supplied, rounding down its size to a multiple + of 3. */ + + int ocount = offsetcount - (offsetcount % 3); + + // FIXME: This is lame that we have to second-guess our caller here. + // The API should change to either fail-hard when we don't have enough offset space + // or that we shouldn't ask our callers to pre-allocate in the first place. + bool using_temporary_offsets = false; + if (re->top_backref > 0 && re->top_backref >= ocount/3) { + ocount = re->top_backref * 3 + 3; + matchBlock.offsetVector = new int[ocount]; + if (!matchBlock.offsetVector) + return JSRegExpErrorNoMemory; + using_temporary_offsets = true; + } else + matchBlock.offsetVector = offsets; + + matchBlock.offsetEnd = ocount; + matchBlock.offsetMax = (2*ocount)/3; + matchBlock.offsetOverflow = false; + + /* Compute the minimum number of offsets that we need to reset each time. Doing + this makes a huge difference to execution time when there aren't many brackets + in the pattern. */ + + int resetcount = 2 + re->top_bracket * 2; + if (resetcount > offsetcount) + resetcount = ocount; + + /* Reset the working variable associated with each extraction. These should + never be used unless previously set, but they get saved and restored, and so we + initialize them to avoid reading uninitialized locations. */ + + if (matchBlock.offsetVector) { + int* iptr = matchBlock.offsetVector + ocount; + int* iend = iptr - resetcount/2 + 1; + while (--iptr >= iend) + *iptr = -1; + } + + /* Set up the first character to match, if available. The first_byte value is + never set for an anchored regular expression, but the anchoring may be forced + at run time, so we have to test for anchoring. The first char may be unset for + an unanchored pattern, of course. If there's no first char and the pattern was + studied, there may be a bitmap of possible first characters. */ + + bool first_byte_caseless = false; + int first_byte = -1; + if (re->options & UseFirstByteOptimizationOption) { + first_byte = re->first_byte & 255; + if ((first_byte_caseless = (re->first_byte & REQ_IGNORE_CASE))) + first_byte = toLowerCase(first_byte); + } + + /* For anchored or unanchored matches, there may be a "last known required + character" set. */ + + bool req_byte_caseless = false; + int req_byte = -1; + int req_byte2 = -1; + if (re->options & UseRequiredByteOptimizationOption) { + req_byte = re->req_byte & 255; // FIXME: This optimization could be made to work for UTF16 chars as well... + req_byte_caseless = (re->req_byte & REQ_IGNORE_CASE); + req_byte2 = flipCase(req_byte); + } + + /* Loop for handling unanchored repeated matching attempts; for anchored regexs + the loop runs just once. */ + + const UChar* startMatch = subject + start_offset; + const UChar* reqBytePtr = startMatch - 1; + bool useMultiLineFirstCharOptimization = re->options & UseMultiLineFirstByteOptimizationOption; + + do { + /* Reset the maximum number of extractions we might see. */ + if (matchBlock.offsetVector) { + int* iptr = matchBlock.offsetVector; + int* iend = iptr + resetcount; + while (iptr < iend) + *iptr++ = -1; + } + + tryFirstByteOptimization(startMatch, endSubject, first_byte, first_byte_caseless, useMultiLineFirstCharOptimization, matchBlock.startSubject + start_offset); + if (tryRequiredByteOptimization(startMatch, endSubject, req_byte, req_byte2, req_byte_caseless, first_byte >= 0, reqBytePtr)) + break; + + /* When a match occurs, substrings will be set for all internal extractions; + we just need to set up the whole thing as substring 0 before returning. If + there were too many extractions, set the return code to zero. In the case + where we had to get some local store to hold offsets for backreferences, copy + those back references that we can. In this case there need not be overflow + if certain parts of the pattern were not used. */ + + /* The code starts after the JSRegExp block and the capture name table. */ + const unsigned char* start_code = reinterpret_cast(re + 1); + + int returnCode = match(startMatch, start_code, 2, matchBlock); + + /* When the result is no match, advance the pointer to the next character + and continue. */ + if (returnCode == 0) { + startMatch++; + continue; + } + + if (returnCode != 1) { + ASSERT(returnCode == JSRegExpErrorHitLimit || returnCode == JSRegExpErrorNoMemory); + DPRINTF((">>>> error: returning %d\n", returnCode)); + return returnCode; + } + + /* We have a match! Copy the offset information from temporary store if + necessary */ + + if (using_temporary_offsets) { + if (offsetcount >= 4) { + memcpy(offsets + 2, matchBlock.offsetVector + 2, (offsetcount - 2) * sizeof(int)); + DPRINTF(("Copied offsets from temporary memory\n")); + } + if (matchBlock.endOffsetTop > offsetcount) + matchBlock.offsetOverflow = true; + + DPRINTF(("Freeing temporary memory\n")); + delete [] matchBlock.offsetVector; + } + + returnCode = matchBlock.offsetOverflow ? 0 : matchBlock.endOffsetTop / 2; + + if (offsetcount < 2) + returnCode = 0; + else { + offsets[0] = startMatch - matchBlock.startSubject; + offsets[1] = matchBlock.endMatchPtr - matchBlock.startSubject; + } + + DPRINTF((">>>> returning %d\n", returnCode)); + return returnCode; + } while (!(re->options & IsAnchoredOption) && startMatch <= endSubject); + + if (using_temporary_offsets) { + DPRINTF(("Freeing temporary memory\n")); + delete [] matchBlock.offsetVector; + } + + DPRINTF((">>>> returning PCRE_ERROR_NOMATCH\n")); + return JSRegExpErrorNoMatch; +} + +} } // namespace dart::jscre diff --git a/runtime/third_party/jscre/pcre_internal.h b/runtime/third_party/jscre/pcre_internal.h new file mode 100644 index 00000000000..002c095b821 --- /dev/null +++ b/runtime/third_party/jscre/pcre_internal.h @@ -0,0 +1,428 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This header contains definitions that are shared between the different +modules, but which are not relevant to the exported API. This includes some +functions whose names all begin with "_pcre_". */ + +#ifndef THIRD_PARTY_JSCRE_PCRE_INTERNAL_H_ +#define THIRD_PARTY_JSCRE_PCRE_INTERNAL_H_ + +#if defined(_WIN32) +typedef unsigned __int32 uint32_t; +#else +#include +#include +#endif + +#include +#include +#include +#include +#include + +/* Bit definitions for entries in the pcre_ctypes table. */ + +#define ctype_space 0x01 +#define ctype_xdigit 0x08 +#define ctype_word 0x10 /* alphameric or '_' */ + +/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set +of bits for a class map. Some classes are built by combining these tables. */ + +#define cbit_space 0 /* \s */ +#define cbit_digit 32 /* \d */ +#define cbit_word 64 /* \w */ +#define cbit_length 96 /* Length of the cbits table */ + +/* Offsets of the various tables from the base tables pointer, and +total length. */ + +#define lcc_offset 0 +#define fcc_offset 128 +#define cbits_offset 256 +#define ctypes_offset (cbits_offset + cbit_length) +#define tables_length (ctypes_offset + 128) + +#ifndef DFTABLES + +// TODO(xxx): Hook this up to something that checks assertions. +#define ASSERT(x) if (!(x)) *(reinterpret_cast(NULL)) = NULL +#define ASSERT_NOT_REACHED() *(reinterpret_cast(NULL)) = NULL + +#ifdef WIN32 +#pragma warning(disable: 4232) +#pragma warning(disable: 4244) +#endif + +#include "./pcre.h" + +/* The value of LINK_SIZE determines the number of bytes used to store links as +offsets within the compiled regex. The default is 2, which allows for compiled +patterns up to 64K long. */ + +#define LINK_SIZE 2 + +/* Define DEBUG to get debugging output on stdout. */ + +#if 0 +#define DEBUG +#endif + +/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef +inline, and there are *still* stupid compilers about that don't like indented +pre-processor statements, or at least there were when I first wrote this. After +all, it had only been about 10 years then... */ + +#ifdef DEBUG +#define DPRINTF(p) printf p +#else +#define DPRINTF(p) /*nothing*/ +#endif + +namespace dart { namespace jscre { + +/* PCRE keeps offsets in its compiled code as 2-byte quantities (always stored +in big-endian order) by default. These are used, for example, to link from the +start of a subpattern to its alternatives and its end. The use of 2 bytes per +offset limits the size of the compiled regex to around 64K, which is big enough +for almost everybody. However, I received a request for an even bigger limit. +For this reason, and also to make the code easier to maintain, the storing and +loading of offsets from the byte string is now handled by the functions that are +defined here. */ + +/* PCRE uses some other 2-byte quantities that do not change when the size of +offsets changes. There are used for repeat counts and for other things such as +capturing parenthesis numbers in back references. */ + +static inline void put2ByteValue(unsigned char* opcodePtr, int value) { + ASSERT(value >= 0 && value <= 0xFFFF); + opcodePtr[0] = value >> 8; + opcodePtr[1] = value; +} + +static inline int get2ByteValue(const unsigned char* opcodePtr) { + return (opcodePtr[0] << 8) | opcodePtr[1]; +} + +static inline void put2ByteValueAndAdvance(unsigned char*& opcodePtr, + int value) { + put2ByteValue(opcodePtr, value); + opcodePtr += 2; +} + +static inline void putLinkValueAllowZero(unsigned char* opcodePtr, + int value) { + put2ByteValue(opcodePtr, value); +} + +static inline int getLinkValueAllowZero(const unsigned char* opcodePtr) { + return get2ByteValue(opcodePtr); +} + +#define MAX_PATTERN_SIZE (1 << 16) + +static inline void putLinkValue(unsigned char* opcodePtr, int value) { + ASSERT(value); + putLinkValueAllowZero(opcodePtr, value); +} + +static inline int getLinkValue(const unsigned char* opcodePtr) { + int value = getLinkValueAllowZero(opcodePtr); + ASSERT(value); + return value; +} + +static inline void putLinkValueAndAdvance(unsigned char*& opcodePtr, + int value) { + putLinkValue(opcodePtr, value); + opcodePtr += LINK_SIZE; +} + +static inline void putLinkValueAllowZeroAndAdvance(unsigned char*& opcodePtr, + int value) { + putLinkValueAllowZero(opcodePtr, value); + opcodePtr += LINK_SIZE; +} + +// FIXME: These are really more of a "compiled regexp state" +// than "regexp options" +enum RegExpOptions { + UseFirstByteOptimizationOption = 0x40000000, /* first_byte is set */ + UseRequiredByteOptimizationOption = 0x20000000, /* req_byte is set */ + UseMultiLineFirstByteOptimizationOption = 0x10000000, + /* start after \n for multiline */ + IsAnchoredOption = 0x02000000, /* can't use partial with this regex */ + IgnoreCaseOption = 0x00000001, + MatchAcrossMultipleLinesOption = 0x00000002 +}; + +/* Flags added to firstbyte or reqbyte; a "non-literal" item is either a +variable-length repeat, or a anything other than literal characters. */ + +#define REQ_IGNORE_CASE 0x0100 /* indicates should ignore case */ +#define REQ_VARY 0x0200 /* reqbyte followed non-literal item */ + +/* Miscellaneous definitions */ + +/* Flag bits and data types for the extended class (OP_XCLASS) for classes that +contain UTF-8 characters with values greater than 255. */ + +#define XCL_NOT 0x01 /* Flag: this is a negative class */ +#define XCL_MAP 0x02 /* Flag: a 32-byte map is present */ + +#define XCL_END 0 /* Marks end of individual items */ +#define XCL_SINGLE 1 /* Single item (one multibyte char) follows */ +#define XCL_RANGE 2 /* A range (two multibyte chars) follows */ + +/* These are escaped items that aren't just an encoding of a particular data +value such as \n. They must have non-zero values, as check_escape() returns +their negation. Also, they must appear in the same order as in the opcode +definitions below, up to ESC_w. The final one must be +ESC_REF as subsequent values are used for \1, \2, \3, etc. There is are two +tests in the code for an escape > ESC_b and <= ESC_w to +detect the types that may be repeated. These are the types that consume +characters. If any new escapes are put in between that don't consume a +character, that code will have to change. */ + +enum { ESC_B = 1, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, ESC_w, ESC_REF }; + +/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets +that extract substrings. Starting from 1 (i.e. after OP_END), the values up to +OP_EOD must correspond in order to the list of escapes immediately above. +Note that whenever this list is updated, the two macro definitions that follow +must also be updated to match. */ + +#define FOR_EACH_OPCODE(macro) \ + macro(END) \ + \ + macro(NOT_WORD_BOUNDARY) \ + macro(WORD_BOUNDARY) \ + macro(NOT_DIGIT) \ + macro(DIGIT) \ + macro(NOT_WHITESPACE) \ + macro(WHITESPACE) \ + macro(NOT_WORDCHAR) \ + macro(WORDCHAR) \ + \ + macro(NOT_NEWLINE) \ + \ + macro(CIRC) \ + macro(DOLL) \ + macro(BOL) \ + macro(EOL) \ + macro(CHAR) \ + macro(CHAR_IGNORING_CASE) \ + macro(ASCII_CHAR) \ + macro(ASCII_LETTER_IGNORING_CASE) \ + macro(NOT) \ + \ + macro(STAR) \ + macro(MINSTAR) \ + macro(PLUS) \ + macro(MINPLUS) \ + macro(QUERY) \ + macro(MINQUERY) \ + macro(UPTO) \ + macro(MINUPTO) \ + macro(EXACT) \ + \ + macro(NOTSTAR) \ + macro(NOTMINSTAR) \ + macro(NOTPLUS) \ + macro(NOTMINPLUS) \ + macro(NOTQUERY) \ + macro(NOTMINQUERY) \ + macro(NOTUPTO) \ + macro(NOTMINUPTO) \ + macro(NOTEXACT) \ + \ + macro(TYPESTAR) \ + macro(TYPEMINSTAR) \ + macro(TYPEPLUS) \ + macro(TYPEMINPLUS) \ + macro(TYPEQUERY) \ + macro(TYPEMINQUERY) \ + macro(TYPEUPTO) \ + macro(TYPEMINUPTO) \ + macro(TYPEEXACT) \ + \ + macro(CRSTAR) \ + macro(CRMINSTAR) \ + macro(CRPLUS) \ + macro(CRMINPLUS) \ + macro(CRQUERY) \ + macro(CRMINQUERY) \ + macro(CRRANGE) \ + macro(CRMINRANGE) \ + \ + macro(CLASS) \ + macro(NCLASS) \ + macro(XCLASS) \ + \ + macro(REF) \ + \ + macro(ALT) \ + macro(KET) \ + macro(KETRMAX) \ + macro(KETRMIN) \ + \ + macro(ASSERT) \ + macro(ASSERT_NOT) \ + \ + macro(BRAZERO) \ + macro(BRAMINZERO) \ + macro(BRANUMBER) \ + macro(BRA) + +#define OPCODE_ENUM_VALUE(opcode) OP_##opcode, +enum { FOR_EACH_OPCODE(OPCODE_ENUM_VALUE) }; + +/* WARNING WARNING WARNING: There is an implicit assumption in pcre.c and +study.c that all opcodes are less than 128 in value. This makes handling UTF-8 +character sequences easier. */ + +/* The highest extraction number before we have to start using additional +bytes. (Originally PCRE didn't have support for extraction counts higher than +this number.) The value is limited by the number of opcodes left after OP_BRA, +i.e. 255 - OP_BRA. We actually set it a bit lower to leave room for additional +opcodes. */ + +/* FIXME: Note that OP_BRA + 100 is > 128, so the two comments above +are in conflict! */ + +#define EXTRACT_BASIC_MAX 100 + +/* The index of names and the +code vector run on as long as necessary after the end. We store an explicit +offset to the name table so that if a regex is compiled on one host, saved, and +then run on another where the size of pointers is different, all might still +be well. For the case of compiled-on-4 and run-on-8, we include an extra +pointer that is always NULL. +*/ + +struct JSRegExp { + uint32_t options; + + uint16_t top_bracket; + uint16_t top_backref; + + uint16_t first_byte; + uint16_t req_byte; +}; + +/* Internal shared data tables. These are tables that are used by more than one + of the exported public functions. They have to be "external" in the C sense, + but are not part of the PCRE public API. The data for these tables is in the + pcre_tables.c module. */ + +#define kjs_pcre_utf8_table1_size 6 + +extern const int kjs_pcre_utf8_table1[6]; +extern const int kjs_pcre_utf8_table2[6]; +extern const int kjs_pcre_utf8_table3[6]; +extern const unsigned char kjs_pcre_utf8_table4[0x40]; + +extern const unsigned char kjs_pcre_default_tables[tables_length]; + +static inline unsigned char toLowerCase(unsigned char c) { + static const unsigned char* lowerCaseChars = + kjs_pcre_default_tables + lcc_offset; + return lowerCaseChars[c]; +} + +static inline unsigned char flipCase(unsigned char c) { + static const unsigned char* flippedCaseChars = + kjs_pcre_default_tables + fcc_offset; + return flippedCaseChars[c]; +} + +static inline unsigned char classBitmapForChar(unsigned char c) { + static const unsigned char* charClassBitmaps = + kjs_pcre_default_tables + cbits_offset; + return charClassBitmaps[c]; +} + +static inline unsigned char charTypeForChar(unsigned char c) { + const unsigned char* charTypeMap = kjs_pcre_default_tables + ctypes_offset; + return charTypeMap[c]; +} + +static inline bool isWordChar(UChar c) { + return c < 128 && (charTypeForChar(c) & ctype_word); +} + +static inline bool isSpaceChar(UChar c) { + return (c < 128 && (charTypeForChar(c) & ctype_space)); +} + +static inline bool isNewline(UChar nl) { + return (nl == 0xA || nl == 0xD || nl == 0x2028 || nl == 0x2029); +} + +static inline bool isBracketStartOpcode(unsigned char opcode) { + if (opcode >= OP_BRA) + return true; + switch (opcode) { + case OP_ASSERT: + case OP_ASSERT_NOT: + return true; + default: + return false; + } +} + +static inline void advanceToEndOfBracket(const unsigned char*& opcodePtr) { + ASSERT(isBracketStartOpcode(*opcodePtr) || *opcodePtr == OP_ALT); + do + opcodePtr += getLinkValue(opcodePtr + 1); + while (*opcodePtr == OP_ALT); +} + +/* Internal shared functions. These are functions that are used in more +that one of the source files. They have to have external linkage, but +but are not part of the public API and so not exported from the library. */ + +extern int kjs_pcre_ucp_othercase(unsigned); +extern bool kjs_pcre_xclass(int, const unsigned char*); + +} } // namespace dart::jscre +#endif + +#endif // THIRD_PARTY_JSCRE_PCRE_INTERNAL_H_ diff --git a/runtime/third_party/jscre/pcre_tables.cpp b/runtime/third_party/jscre/pcre_tables.cpp new file mode 100644 index 00000000000..943b80ce43e --- /dev/null +++ b/runtime/third_party/jscre/pcre_tables.cpp @@ -0,0 +1,75 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This module contains some fixed tables that are used by more than one of the +PCRE code modules. */ + +#include "pcre_internal.h" + +namespace dart { namespace jscre { + +/************************************************* +* Tables for UTF-8 support * +*************************************************/ + +/* These are the breakpoints for different numbers of bytes in a UTF-8 +character. */ + +const int kjs_pcre_utf8_table1[6] = + { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff}; + +/* These are the indicator bits and the mask for the data bits to set in the +first byte of a character, indexed by the number of additional bytes. */ + +const int kjs_pcre_utf8_table2[6] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}; +const int kjs_pcre_utf8_table3[6] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01}; + +/* Table of the number of extra characters, indexed by the first character +masked with 0x3f. The highest number for a valid UTF-8 character is in fact +0x3d. */ + +const unsigned char kjs_pcre_utf8_table4[0x40] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; + +#include "pcre_chartables.c" + +} } // namespace dart::jscre diff --git a/runtime/third_party/jscre/pcre_ucp_searchfuncs.cpp b/runtime/third_party/jscre/pcre_ucp_searchfuncs.cpp new file mode 100644 index 00000000000..07fa42b36e8 --- /dev/null +++ b/runtime/third_party/jscre/pcre_ucp_searchfuncs.cpp @@ -0,0 +1,102 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains code for searching the table of Unicode character +properties. */ + +#include "pcre_internal.h" + +#include "ucpinternal.h" /* Internal table details */ +#include "ucptable.cpp" /* The table itself */ + +namespace dart { namespace jscre { + +/************************************************* +* Search table and return other case * +*************************************************/ + +/* If the given character is a letter, and there is another case for the +letter, return the other case. Otherwise, return -1. + +Arguments: + c the character value + +Returns: the other case or -1 if none +*/ + +int kjs_pcre_ucp_othercase(unsigned c) +{ + int bot = 0; + int top = sizeof(ucp_table) / sizeof(cnode); + int mid; + + /* The table is searched using a binary chop. You might think that using + intermediate variables to hold some of the common expressions would speed + things up, but tests with gcc 3.4.4 on Linux showed that, on the contrary, it + makes things a lot slower. */ + + for (;;) { + if (top <= bot) + return -1; + mid = (bot + top) >> 1; + if (c == (ucp_table[mid].f0 & f0_charmask)) + break; + if (c < (ucp_table[mid].f0 & f0_charmask)) + top = mid; + else { + if ((ucp_table[mid].f0 & f0_rangeflag) && (c <= (ucp_table[mid].f0 & f0_charmask) + (ucp_table[mid].f1 & f1_rangemask))) + break; + bot = mid + 1; + } + } + + /* Found an entry in the table. Return -1 for a range entry. Otherwise return + the other case if there is one, else -1. */ + + if (ucp_table[mid].f0 & f0_rangeflag) + return -1; + + int offset = ucp_table[mid].f1 & f1_casemask; + if (offset & f1_caseneg) + offset |= f1_caseneg; + return !offset ? -1 : c + offset; +} + +} } // namespace dart::jscre diff --git a/runtime/third_party/jscre/pcre_xclass.cpp b/runtime/third_party/jscre/pcre_xclass.cpp new file mode 100644 index 00000000000..5d170e5dfc9 --- /dev/null +++ b/runtime/third_party/jscre/pcre_xclass.cpp @@ -0,0 +1,118 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This module contains an internal function that is used to match an extended +class (one that contains characters whose values are > 255). */ + +#include "pcre_internal.h" + +namespace dart { namespace jscre { + +/************************************************* +* Match character against an XCLASS * +*************************************************/ + +/* This function is called to match a character against an extended class that +might contain values > 255. + +Arguments: + c the character + data points to the flag byte of the XCLASS data + +Returns: true if character matches, else false +*/ + +/* Get the next UTF-8 character, advancing the pointer. This is called when we + know we are in UTF-8 mode. */ + +static inline void getUTF8CharAndAdvancePointer(int& c, const unsigned char*& subjectPtr) +{ + c = *subjectPtr++; + if ((c & 0xc0) == 0xc0) { + int gcaa = kjs_pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ + int gcss = 6 * gcaa; + c = (c & kjs_pcre_utf8_table3[gcaa]) << gcss; + while (gcaa-- > 0) { + gcss -= 6; + c |= (*subjectPtr++ & 0x3f) << gcss; + } + } +} + +bool kjs_pcre_xclass(int c, const unsigned char* data) +{ + bool negated = (*data & XCL_NOT); + + /* Character values < 256 are matched against a bitmap, if one is present. If + not, we still carry on, because there may be ranges that start below 256 in the + additional data. */ + + if (c < 256) { + if ((*data & XCL_MAP) != 0 && (data[1 + c/8] & (1 << (c&7))) != 0) + return !negated; /* char found */ + } + + /* First skip the bit map if present. Then match against the list of Unicode + properties or large chars or ranges that end with a large char. We won't ever + encounter XCL_PROP or XCL_NOTPROP when UCP support is not compiled. */ + + if ((*data++ & XCL_MAP) != 0) + data += 32; + + int t; + while ((t = *data++) != XCL_END) { + if (t == XCL_SINGLE) { + int x; + getUTF8CharAndAdvancePointer(x, data); + if (c == x) + return !negated; + } + else if (t == XCL_RANGE) { + int x, y; + getUTF8CharAndAdvancePointer(x, data); + getUTF8CharAndAdvancePointer(y, data); + if (c >= x && c <= y) + return !negated; + } + } + + return negated; /* char did not match */ +} + +} } // namespace dart::jscre diff --git a/runtime/third_party/jscre/ucpinternal.h b/runtime/third_party/jscre/ucpinternal.h new file mode 100644 index 00000000000..c7b3c46396b --- /dev/null +++ b/runtime/third_party/jscre/ucpinternal.h @@ -0,0 +1,130 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +#ifndef THIRD_PARTY_JSCRE_UCPINTERNAL_H_ +#define THIRD_PARTY_JSCRE_UCPINTERNAL_H_ + +/************************************************* +* Unicode Property Table handler * +*************************************************/ + +/* Internal header file defining the layout of the bits in each pair of 32-bit +words that form a data item in the table. */ + +typedef struct cnode { + unsigned f0; + unsigned f1; +} cnode; + +/* Things for the f0 field */ + +#define f0_scriptmask 0xff000000 /* Mask for script field */ +#define f0_scriptshift 24 /* Shift for script value */ +#define f0_rangeflag 0x00f00000 /* Flag for a range item */ +#define f0_charmask 0x001fffff /* Mask for code point value */ + +/* Things for the f1 field */ + +#define f1_typemask 0xfc000000 /* Mask for char type field */ +#define f1_typeshift 26 /* Shift for the type field */ +#define f1_rangemask 0x0000ffff /* Mask for a range offset */ +#define f1_casemask 0x0000ffff /* Mask for a case offset */ +#define f1_caseneg 0xffff8000 /* Bits for negation */ + +/* The data consists of a vector of structures of type cnode. The two unsigned +32-bit integers are used as follows: + +(f0) (1) The most significant byte holds the script number. The numbers are + defined by the enum in ucp.h. + + (2) The 0x00800000 bit is set if this entry defines a range of characters. + It is not set if this entry defines a single character + + (3) The 0x00600000 bits are spare. + + (4) The 0x001fffff bits contain the code point. No Unicode code point will + ever be greater than 0x0010ffff, so this should be OK for ever. + +(f1) (1) The 0xfc000000 bits contain the character type number. The numbers are + defined by an enum in ucp.h. + + (2) The 0x03ff0000 bits are spare. + + (3) The 0x0000ffff bits contain EITHER the unsigned offset to the top of + range if this entry defines a range, OR the *signed* offset to the + character's "other case" partner if this entry defines a single + character. There is no partner if the value is zero. + +------------------------------------------------------------------------------- +| script (8) |.|.|.| codepoint (21) || type (6) |.|.| spare (8) | offset (16) | +------------------------------------------------------------------------------- + | | | | | + | | |-> spare | |-> spare + | | | + | |-> spare |-> spare + | + |-> range flag + +The upper/lower casing information is set only for characters that come in +pairs. The non-one-to-one mappings in the Unicode data are ignored. + +When searching the data, proceed as follows: + +(1) Set up for a binary chop search. + +(2) If the top is not greater than the bottom, the character is not in the + table. Its type must therefore be "Cn" ("Undefined"). + +(3) Find the middle vector element. + +(4) Extract the code point and compare. If equal, we are done. + +(5) If the test character is smaller, set the top to the current point, and + goto (2). + +(6) If the current entry defines a range, compute the last character by adding + the offset, and see if the test character is within the range. If it is, + we are done. + +(7) Otherwise, set the bottom to one element past the current point and goto + (2). +*/ + +/* End of ucpinternal.h */ +#endif // THIRD_PARTY_JSCRE_UCPINTERNAL_H_ diff --git a/runtime/third_party/jscre/ucptable.cpp b/runtime/third_party/jscre/ucptable.cpp new file mode 100644 index 00000000000..011f7f57244 --- /dev/null +++ b/runtime/third_party/jscre/ucptable.cpp @@ -0,0 +1,2968 @@ +/* This source module is automatically generated from the Unicode +property table. See ucpinternal.h for a description of the layout. */ + +static const cnode ucp_table[] = { + { 0x09800000, 0x0000001f }, + { 0x09000020, 0x74000000 }, + { 0x09800021, 0x54000002 }, + { 0x09000024, 0x5c000000 }, + { 0x09800025, 0x54000002 }, + { 0x09000028, 0x58000000 }, + { 0x09000029, 0x48000000 }, + { 0x0900002a, 0x54000000 }, + { 0x0900002b, 0x64000000 }, + { 0x0900002c, 0x54000000 }, + { 0x0900002d, 0x44000000 }, + { 0x0980002e, 0x54000001 }, + { 0x09800030, 0x34000009 }, + { 0x0980003a, 0x54000001 }, + { 0x0980003c, 0x64000002 }, + { 0x0980003f, 0x54000001 }, + { 0x21000041, 0x24000020 }, + { 0x21000042, 0x24000020 }, + { 0x21000043, 0x24000020 }, + { 0x21000044, 0x24000020 }, + { 0x21000045, 0x24000020 }, + { 0x21000046, 0x24000020 }, + { 0x21000047, 0x24000020 }, + { 0x21000048, 0x24000020 }, + { 0x21000049, 0x24000020 }, + { 0x2100004a, 0x24000020 }, + { 0x2100004b, 0x24000020 }, + { 0x2100004c, 0x24000020 }, + { 0x2100004d, 0x24000020 }, + { 0x2100004e, 0x24000020 }, + { 0x2100004f, 0x24000020 }, + { 0x21000050, 0x24000020 }, + { 0x21000051, 0x24000020 }, + { 0x21000052, 0x24000020 }, + { 0x21000053, 0x24000020 }, + { 0x21000054, 0x24000020 }, + { 0x21000055, 0x24000020 }, + { 0x21000056, 0x24000020 }, + { 0x21000057, 0x24000020 }, + { 0x21000058, 0x24000020 }, + { 0x21000059, 0x24000020 }, + { 0x2100005a, 0x24000020 }, + { 0x0900005b, 0x58000000 }, + { 0x0900005c, 0x54000000 }, + { 0x0900005d, 0x48000000 }, + { 0x0900005e, 0x60000000 }, + { 0x0900005f, 0x40000000 }, + { 0x09000060, 0x60000000 }, + { 0x21000061, 0x1400ffe0 }, + { 0x21000062, 0x1400ffe0 }, + { 0x21000063, 0x1400ffe0 }, + { 0x21000064, 0x1400ffe0 }, + { 0x21000065, 0x1400ffe0 }, + { 0x21000066, 0x1400ffe0 }, + { 0x21000067, 0x1400ffe0 }, + { 0x21000068, 0x1400ffe0 }, + { 0x21000069, 0x1400ffe0 }, + { 0x2100006a, 0x1400ffe0 }, + { 0x2100006b, 0x1400ffe0 }, + { 0x2100006c, 0x1400ffe0 }, + { 0x2100006d, 0x1400ffe0 }, + { 0x2100006e, 0x1400ffe0 }, + { 0x2100006f, 0x1400ffe0 }, + { 0x21000070, 0x1400ffe0 }, + { 0x21000071, 0x1400ffe0 }, + { 0x21000072, 0x1400ffe0 }, + { 0x21000073, 0x1400ffe0 }, + { 0x21000074, 0x1400ffe0 }, + { 0x21000075, 0x1400ffe0 }, + { 0x21000076, 0x1400ffe0 }, + { 0x21000077, 0x1400ffe0 }, + { 0x21000078, 0x1400ffe0 }, + { 0x21000079, 0x1400ffe0 }, + { 0x2100007a, 0x1400ffe0 }, + { 0x0900007b, 0x58000000 }, + { 0x0900007c, 0x64000000 }, + { 0x0900007d, 0x48000000 }, + { 0x0900007e, 0x64000000 }, + { 0x0980007f, 0x00000020 }, + { 0x090000a0, 0x74000000 }, + { 0x090000a1, 0x54000000 }, + { 0x098000a2, 0x5c000003 }, + { 0x098000a6, 0x68000001 }, + { 0x090000a8, 0x60000000 }, + { 0x090000a9, 0x68000000 }, + { 0x210000aa, 0x14000000 }, + { 0x090000ab, 0x50000000 }, + { 0x090000ac, 0x64000000 }, + { 0x090000ad, 0x04000000 }, + { 0x090000ae, 0x68000000 }, + { 0x090000af, 0x60000000 }, + { 0x090000b0, 0x68000000 }, + { 0x090000b1, 0x64000000 }, + { 0x098000b2, 0x3c000001 }, + { 0x090000b4, 0x60000000 }, + { 0x090000b5, 0x140002e7 }, + { 0x090000b6, 0x68000000 }, + { 0x090000b7, 0x54000000 }, + { 0x090000b8, 0x60000000 }, + { 0x090000b9, 0x3c000000 }, + { 0x210000ba, 0x14000000 }, + { 0x090000bb, 0x4c000000 }, + { 0x098000bc, 0x3c000002 }, + { 0x090000bf, 0x54000000 }, + { 0x210000c0, 0x24000020 }, + { 0x210000c1, 0x24000020 }, + { 0x210000c2, 0x24000020 }, + { 0x210000c3, 0x24000020 }, + { 0x210000c4, 0x24000020 }, + { 0x210000c5, 0x24000020 }, + { 0x210000c6, 0x24000020 }, + { 0x210000c7, 0x24000020 }, + { 0x210000c8, 0x24000020 }, + { 0x210000c9, 0x24000020 }, + { 0x210000ca, 0x24000020 }, + { 0x210000cb, 0x24000020 }, + { 0x210000cc, 0x24000020 }, + { 0x210000cd, 0x24000020 }, + { 0x210000ce, 0x24000020 }, + { 0x210000cf, 0x24000020 }, + { 0x210000d0, 0x24000020 }, + { 0x210000d1, 0x24000020 }, + { 0x210000d2, 0x24000020 }, + { 0x210000d3, 0x24000020 }, + { 0x210000d4, 0x24000020 }, + { 0x210000d5, 0x24000020 }, + { 0x210000d6, 0x24000020 }, + { 0x090000d7, 0x64000000 }, + { 0x210000d8, 0x24000020 }, + { 0x210000d9, 0x24000020 }, + { 0x210000da, 0x24000020 }, + { 0x210000db, 0x24000020 }, + { 0x210000dc, 0x24000020 }, + { 0x210000dd, 0x24000020 }, + { 0x210000de, 0x24000020 }, + { 0x210000df, 0x14000000 }, + { 0x210000e0, 0x1400ffe0 }, + { 0x210000e1, 0x1400ffe0 }, + { 0x210000e2, 0x1400ffe0 }, + { 0x210000e3, 0x1400ffe0 }, + { 0x210000e4, 0x1400ffe0 }, + { 0x210000e5, 0x1400ffe0 }, + { 0x210000e6, 0x1400ffe0 }, + { 0x210000e7, 0x1400ffe0 }, + { 0x210000e8, 0x1400ffe0 }, + { 0x210000e9, 0x1400ffe0 }, + { 0x210000ea, 0x1400ffe0 }, + { 0x210000eb, 0x1400ffe0 }, + { 0x210000ec, 0x1400ffe0 }, + { 0x210000ed, 0x1400ffe0 }, + { 0x210000ee, 0x1400ffe0 }, + { 0x210000ef, 0x1400ffe0 }, + { 0x210000f0, 0x1400ffe0 }, + { 0x210000f1, 0x1400ffe0 }, + { 0x210000f2, 0x1400ffe0 }, + { 0x210000f3, 0x1400ffe0 }, + { 0x210000f4, 0x1400ffe0 }, + { 0x210000f5, 0x1400ffe0 }, + { 0x210000f6, 0x1400ffe0 }, + { 0x090000f7, 0x64000000 }, + { 0x210000f8, 0x1400ffe0 }, + { 0x210000f9, 0x1400ffe0 }, + { 0x210000fa, 0x1400ffe0 }, + { 0x210000fb, 0x1400ffe0 }, + { 0x210000fc, 0x1400ffe0 }, + { 0x210000fd, 0x1400ffe0 }, + { 0x210000fe, 0x1400ffe0 }, + { 0x210000ff, 0x14000079 }, + { 0x21000100, 0x24000001 }, + { 0x21000101, 0x1400ffff }, + { 0x21000102, 0x24000001 }, + { 0x21000103, 0x1400ffff }, + { 0x21000104, 0x24000001 }, + { 0x21000105, 0x1400ffff }, + { 0x21000106, 0x24000001 }, + { 0x21000107, 0x1400ffff }, + { 0x21000108, 0x24000001 }, + { 0x21000109, 0x1400ffff }, + { 0x2100010a, 0x24000001 }, + { 0x2100010b, 0x1400ffff }, + { 0x2100010c, 0x24000001 }, + { 0x2100010d, 0x1400ffff }, + { 0x2100010e, 0x24000001 }, + { 0x2100010f, 0x1400ffff }, + { 0x21000110, 0x24000001 }, + { 0x21000111, 0x1400ffff }, + { 0x21000112, 0x24000001 }, + { 0x21000113, 0x1400ffff }, + { 0x21000114, 0x24000001 }, + { 0x21000115, 0x1400ffff }, + { 0x21000116, 0x24000001 }, + { 0x21000117, 0x1400ffff }, + { 0x21000118, 0x24000001 }, + { 0x21000119, 0x1400ffff }, + { 0x2100011a, 0x24000001 }, + { 0x2100011b, 0x1400ffff }, + { 0x2100011c, 0x24000001 }, + { 0x2100011d, 0x1400ffff }, + { 0x2100011e, 0x24000001 }, + { 0x2100011f, 0x1400ffff }, + { 0x21000120, 0x24000001 }, + { 0x21000121, 0x1400ffff }, + { 0x21000122, 0x24000001 }, + { 0x21000123, 0x1400ffff }, + { 0x21000124, 0x24000001 }, + { 0x21000125, 0x1400ffff }, + { 0x21000126, 0x24000001 }, + { 0x21000127, 0x1400ffff }, + { 0x21000128, 0x24000001 }, + { 0x21000129, 0x1400ffff }, + { 0x2100012a, 0x24000001 }, + { 0x2100012b, 0x1400ffff }, + { 0x2100012c, 0x24000001 }, + { 0x2100012d, 0x1400ffff }, + { 0x2100012e, 0x24000001 }, + { 0x2100012f, 0x1400ffff }, + { 0x21000130, 0x2400ff39 }, + { 0x21000131, 0x1400ff18 }, + { 0x21000132, 0x24000001 }, + { 0x21000133, 0x1400ffff }, + { 0x21000134, 0x24000001 }, + { 0x21000135, 0x1400ffff }, + { 0x21000136, 0x24000001 }, + { 0x21000137, 0x1400ffff }, + { 0x21000138, 0x14000000 }, + { 0x21000139, 0x24000001 }, + { 0x2100013a, 0x1400ffff }, + { 0x2100013b, 0x24000001 }, + { 0x2100013c, 0x1400ffff }, + { 0x2100013d, 0x24000001 }, + { 0x2100013e, 0x1400ffff }, + { 0x2100013f, 0x24000001 }, + { 0x21000140, 0x1400ffff }, + { 0x21000141, 0x24000001 }, + { 0x21000142, 0x1400ffff }, + { 0x21000143, 0x24000001 }, + { 0x21000144, 0x1400ffff }, + { 0x21000145, 0x24000001 }, + { 0x21000146, 0x1400ffff }, + { 0x21000147, 0x24000001 }, + { 0x21000148, 0x1400ffff }, + { 0x21000149, 0x14000000 }, + { 0x2100014a, 0x24000001 }, + { 0x2100014b, 0x1400ffff }, + { 0x2100014c, 0x24000001 }, + { 0x2100014d, 0x1400ffff }, + { 0x2100014e, 0x24000001 }, + { 0x2100014f, 0x1400ffff }, + { 0x21000150, 0x24000001 }, + { 0x21000151, 0x1400ffff }, + { 0x21000152, 0x24000001 }, + { 0x21000153, 0x1400ffff }, + { 0x21000154, 0x24000001 }, + { 0x21000155, 0x1400ffff }, + { 0x21000156, 0x24000001 }, + { 0x21000157, 0x1400ffff }, + { 0x21000158, 0x24000001 }, + { 0x21000159, 0x1400ffff }, + { 0x2100015a, 0x24000001 }, + { 0x2100015b, 0x1400ffff }, + { 0x2100015c, 0x24000001 }, + { 0x2100015d, 0x1400ffff }, + { 0x2100015e, 0x24000001 }, + { 0x2100015f, 0x1400ffff }, + { 0x21000160, 0x24000001 }, + { 0x21000161, 0x1400ffff }, + { 0x21000162, 0x24000001 }, + { 0x21000163, 0x1400ffff }, + { 0x21000164, 0x24000001 }, + { 0x21000165, 0x1400ffff }, + { 0x21000166, 0x24000001 }, + { 0x21000167, 0x1400ffff }, + { 0x21000168, 0x24000001 }, + { 0x21000169, 0x1400ffff }, + { 0x2100016a, 0x24000001 }, + { 0x2100016b, 0x1400ffff }, + { 0x2100016c, 0x24000001 }, + { 0x2100016d, 0x1400ffff }, + { 0x2100016e, 0x24000001 }, + { 0x2100016f, 0x1400ffff }, + { 0x21000170, 0x24000001 }, + { 0x21000171, 0x1400ffff }, + { 0x21000172, 0x24000001 }, + { 0x21000173, 0x1400ffff }, + { 0x21000174, 0x24000001 }, + { 0x21000175, 0x1400ffff }, + { 0x21000176, 0x24000001 }, + { 0x21000177, 0x1400ffff }, + { 0x21000178, 0x2400ff87 }, + { 0x21000179, 0x24000001 }, + { 0x2100017a, 0x1400ffff }, + { 0x2100017b, 0x24000001 }, + { 0x2100017c, 0x1400ffff }, + { 0x2100017d, 0x24000001 }, + { 0x2100017e, 0x1400ffff }, + { 0x2100017f, 0x1400fed4 }, + { 0x21000180, 0x14000000 }, + { 0x21000181, 0x240000d2 }, + { 0x21000182, 0x24000001 }, + { 0x21000183, 0x1400ffff }, + { 0x21000184, 0x24000001 }, + { 0x21000185, 0x1400ffff }, + { 0x21000186, 0x240000ce }, + { 0x21000187, 0x24000001 }, + { 0x21000188, 0x1400ffff }, + { 0x21000189, 0x240000cd }, + { 0x2100018a, 0x240000cd }, + { 0x2100018b, 0x24000001 }, + { 0x2100018c, 0x1400ffff }, + { 0x2100018d, 0x14000000 }, + { 0x2100018e, 0x2400004f }, + { 0x2100018f, 0x240000ca }, + { 0x21000190, 0x240000cb }, + { 0x21000191, 0x24000001 }, + { 0x21000192, 0x1400ffff }, + { 0x21000193, 0x240000cd }, + { 0x21000194, 0x240000cf }, + { 0x21000195, 0x14000061 }, + { 0x21000196, 0x240000d3 }, + { 0x21000197, 0x240000d1 }, + { 0x21000198, 0x24000001 }, + { 0x21000199, 0x1400ffff }, + { 0x2100019a, 0x140000a3 }, + { 0x2100019b, 0x14000000 }, + { 0x2100019c, 0x240000d3 }, + { 0x2100019d, 0x240000d5 }, + { 0x2100019e, 0x14000082 }, + { 0x2100019f, 0x240000d6 }, + { 0x210001a0, 0x24000001 }, + { 0x210001a1, 0x1400ffff }, + { 0x210001a2, 0x24000001 }, + { 0x210001a3, 0x1400ffff }, + { 0x210001a4, 0x24000001 }, + { 0x210001a5, 0x1400ffff }, + { 0x210001a6, 0x240000da }, + { 0x210001a7, 0x24000001 }, + { 0x210001a8, 0x1400ffff }, + { 0x210001a9, 0x240000da }, + { 0x218001aa, 0x14000001 }, + { 0x210001ac, 0x24000001 }, + { 0x210001ad, 0x1400ffff }, + { 0x210001ae, 0x240000da }, + { 0x210001af, 0x24000001 }, + { 0x210001b0, 0x1400ffff }, + { 0x210001b1, 0x240000d9 }, + { 0x210001b2, 0x240000d9 }, + { 0x210001b3, 0x24000001 }, + { 0x210001b4, 0x1400ffff }, + { 0x210001b5, 0x24000001 }, + { 0x210001b6, 0x1400ffff }, + { 0x210001b7, 0x240000db }, + { 0x210001b8, 0x24000001 }, + { 0x210001b9, 0x1400ffff }, + { 0x210001ba, 0x14000000 }, + { 0x210001bb, 0x1c000000 }, + { 0x210001bc, 0x24000001 }, + { 0x210001bd, 0x1400ffff }, + { 0x210001be, 0x14000000 }, + { 0x210001bf, 0x14000038 }, + { 0x218001c0, 0x1c000003 }, + { 0x210001c4, 0x24000002 }, + { 0x210001c5, 0x2000ffff }, + { 0x210001c6, 0x1400fffe }, + { 0x210001c7, 0x24000002 }, + { 0x210001c8, 0x2000ffff }, + { 0x210001c9, 0x1400fffe }, + { 0x210001ca, 0x24000002 }, + { 0x210001cb, 0x2000ffff }, + { 0x210001cc, 0x1400fffe }, + { 0x210001cd, 0x24000001 }, + { 0x210001ce, 0x1400ffff }, + { 0x210001cf, 0x24000001 }, + { 0x210001d0, 0x1400ffff }, + { 0x210001d1, 0x24000001 }, + { 0x210001d2, 0x1400ffff }, + { 0x210001d3, 0x24000001 }, + { 0x210001d4, 0x1400ffff }, + { 0x210001d5, 0x24000001 }, + { 0x210001d6, 0x1400ffff }, + { 0x210001d7, 0x24000001 }, + { 0x210001d8, 0x1400ffff }, + { 0x210001d9, 0x24000001 }, + { 0x210001da, 0x1400ffff }, + { 0x210001db, 0x24000001 }, + { 0x210001dc, 0x1400ffff }, + { 0x210001dd, 0x1400ffb1 }, + { 0x210001de, 0x24000001 }, + { 0x210001df, 0x1400ffff }, + { 0x210001e0, 0x24000001 }, + { 0x210001e1, 0x1400ffff }, + { 0x210001e2, 0x24000001 }, + { 0x210001e3, 0x1400ffff }, + { 0x210001e4, 0x24000001 }, + { 0x210001e5, 0x1400ffff }, + { 0x210001e6, 0x24000001 }, + { 0x210001e7, 0x1400ffff }, + { 0x210001e8, 0x24000001 }, + { 0x210001e9, 0x1400ffff }, + { 0x210001ea, 0x24000001 }, + { 0x210001eb, 0x1400ffff }, + { 0x210001ec, 0x24000001 }, + { 0x210001ed, 0x1400ffff }, + { 0x210001ee, 0x24000001 }, + { 0x210001ef, 0x1400ffff }, + { 0x210001f0, 0x14000000 }, + { 0x210001f1, 0x24000002 }, + { 0x210001f2, 0x2000ffff }, + { 0x210001f3, 0x1400fffe }, + { 0x210001f4, 0x24000001 }, + { 0x210001f5, 0x1400ffff }, + { 0x210001f6, 0x2400ff9f }, + { 0x210001f7, 0x2400ffc8 }, + { 0x210001f8, 0x24000001 }, + { 0x210001f9, 0x1400ffff }, + { 0x210001fa, 0x24000001 }, + { 0x210001fb, 0x1400ffff }, + { 0x210001fc, 0x24000001 }, + { 0x210001fd, 0x1400ffff }, + { 0x210001fe, 0x24000001 }, + { 0x210001ff, 0x1400ffff }, + { 0x21000200, 0x24000001 }, + { 0x21000201, 0x1400ffff }, + { 0x21000202, 0x24000001 }, + { 0x21000203, 0x1400ffff }, + { 0x21000204, 0x24000001 }, + { 0x21000205, 0x1400ffff }, + { 0x21000206, 0x24000001 }, + { 0x21000207, 0x1400ffff }, + { 0x21000208, 0x24000001 }, + { 0x21000209, 0x1400ffff }, + { 0x2100020a, 0x24000001 }, + { 0x2100020b, 0x1400ffff }, + { 0x2100020c, 0x24000001 }, + { 0x2100020d, 0x1400ffff }, + { 0x2100020e, 0x24000001 }, + { 0x2100020f, 0x1400ffff }, + { 0x21000210, 0x24000001 }, + { 0x21000211, 0x1400ffff }, + { 0x21000212, 0x24000001 }, + { 0x21000213, 0x1400ffff }, + { 0x21000214, 0x24000001 }, + { 0x21000215, 0x1400ffff }, + { 0x21000216, 0x24000001 }, + { 0x21000217, 0x1400ffff }, + { 0x21000218, 0x24000001 }, + { 0x21000219, 0x1400ffff }, + { 0x2100021a, 0x24000001 }, + { 0x2100021b, 0x1400ffff }, + { 0x2100021c, 0x24000001 }, + { 0x2100021d, 0x1400ffff }, + { 0x2100021e, 0x24000001 }, + { 0x2100021f, 0x1400ffff }, + { 0x21000220, 0x2400ff7e }, + { 0x21000221, 0x14000000 }, + { 0x21000222, 0x24000001 }, + { 0x21000223, 0x1400ffff }, + { 0x21000224, 0x24000001 }, + { 0x21000225, 0x1400ffff }, + { 0x21000226, 0x24000001 }, + { 0x21000227, 0x1400ffff }, + { 0x21000228, 0x24000001 }, + { 0x21000229, 0x1400ffff }, + { 0x2100022a, 0x24000001 }, + { 0x2100022b, 0x1400ffff }, + { 0x2100022c, 0x24000001 }, + { 0x2100022d, 0x1400ffff }, + { 0x2100022e, 0x24000001 }, + { 0x2100022f, 0x1400ffff }, + { 0x21000230, 0x24000001 }, + { 0x21000231, 0x1400ffff }, + { 0x21000232, 0x24000001 }, + { 0x21000233, 0x1400ffff }, + { 0x21800234, 0x14000005 }, + { 0x2100023a, 0x24000000 }, + { 0x2100023b, 0x24000001 }, + { 0x2100023c, 0x1400ffff }, + { 0x2100023d, 0x2400ff5d }, + { 0x2100023e, 0x24000000 }, + { 0x2180023f, 0x14000001 }, + { 0x21000241, 0x24000053 }, + { 0x21800250, 0x14000002 }, + { 0x21000253, 0x1400ff2e }, + { 0x21000254, 0x1400ff32 }, + { 0x21000255, 0x14000000 }, + { 0x21000256, 0x1400ff33 }, + { 0x21000257, 0x1400ff33 }, + { 0x21000258, 0x14000000 }, + { 0x21000259, 0x1400ff36 }, + { 0x2100025a, 0x14000000 }, + { 0x2100025b, 0x1400ff35 }, + { 0x2180025c, 0x14000003 }, + { 0x21000260, 0x1400ff33 }, + { 0x21800261, 0x14000001 }, + { 0x21000263, 0x1400ff31 }, + { 0x21800264, 0x14000003 }, + { 0x21000268, 0x1400ff2f }, + { 0x21000269, 0x1400ff2d }, + { 0x2180026a, 0x14000004 }, + { 0x2100026f, 0x1400ff2d }, + { 0x21800270, 0x14000001 }, + { 0x21000272, 0x1400ff2b }, + { 0x21800273, 0x14000001 }, + { 0x21000275, 0x1400ff2a }, + { 0x21800276, 0x14000009 }, + { 0x21000280, 0x1400ff26 }, + { 0x21800281, 0x14000001 }, + { 0x21000283, 0x1400ff26 }, + { 0x21800284, 0x14000003 }, + { 0x21000288, 0x1400ff26 }, + { 0x21000289, 0x14000000 }, + { 0x2100028a, 0x1400ff27 }, + { 0x2100028b, 0x1400ff27 }, + { 0x2180028c, 0x14000005 }, + { 0x21000292, 0x1400ff25 }, + { 0x21000293, 0x14000000 }, + { 0x21000294, 0x1400ffad }, + { 0x21800295, 0x1400001a }, + { 0x218002b0, 0x18000011 }, + { 0x098002c2, 0x60000003 }, + { 0x098002c6, 0x1800000b }, + { 0x098002d2, 0x6000000d }, + { 0x218002e0, 0x18000004 }, + { 0x098002e5, 0x60000008 }, + { 0x090002ee, 0x18000000 }, + { 0x098002ef, 0x60000010 }, + { 0x1b800300, 0x30000044 }, + { 0x1b000345, 0x30000054 }, + { 0x1b800346, 0x30000029 }, + { 0x13800374, 0x60000001 }, + { 0x1300037a, 0x18000000 }, + { 0x0900037e, 0x54000000 }, + { 0x13800384, 0x60000001 }, + { 0x13000386, 0x24000026 }, + { 0x09000387, 0x54000000 }, + { 0x13000388, 0x24000025 }, + { 0x13000389, 0x24000025 }, + { 0x1300038a, 0x24000025 }, + { 0x1300038c, 0x24000040 }, + { 0x1300038e, 0x2400003f }, + { 0x1300038f, 0x2400003f }, + { 0x13000390, 0x14000000 }, + { 0x13000391, 0x24000020 }, + { 0x13000392, 0x24000020 }, + { 0x13000393, 0x24000020 }, + { 0x13000394, 0x24000020 }, + { 0x13000395, 0x24000020 }, + { 0x13000396, 0x24000020 }, + { 0x13000397, 0x24000020 }, + { 0x13000398, 0x24000020 }, + { 0x13000399, 0x24000020 }, + { 0x1300039a, 0x24000020 }, + { 0x1300039b, 0x24000020 }, + { 0x1300039c, 0x24000020 }, + { 0x1300039d, 0x24000020 }, + { 0x1300039e, 0x24000020 }, + { 0x1300039f, 0x24000020 }, + { 0x130003a0, 0x24000020 }, + { 0x130003a1, 0x24000020 }, + { 0x130003a3, 0x24000020 }, + { 0x130003a4, 0x24000020 }, + { 0x130003a5, 0x24000020 }, + { 0x130003a6, 0x24000020 }, + { 0x130003a7, 0x24000020 }, + { 0x130003a8, 0x24000020 }, + { 0x130003a9, 0x24000020 }, + { 0x130003aa, 0x24000020 }, + { 0x130003ab, 0x24000020 }, + { 0x130003ac, 0x1400ffda }, + { 0x130003ad, 0x1400ffdb }, + { 0x130003ae, 0x1400ffdb }, + { 0x130003af, 0x1400ffdb }, + { 0x130003b0, 0x14000000 }, + { 0x130003b1, 0x1400ffe0 }, + { 0x130003b2, 0x1400ffe0 }, + { 0x130003b3, 0x1400ffe0 }, + { 0x130003b4, 0x1400ffe0 }, + { 0x130003b5, 0x1400ffe0 }, + { 0x130003b6, 0x1400ffe0 }, + { 0x130003b7, 0x1400ffe0 }, + { 0x130003b8, 0x1400ffe0 }, + { 0x130003b9, 0x1400ffe0 }, + { 0x130003ba, 0x1400ffe0 }, + { 0x130003bb, 0x1400ffe0 }, + { 0x130003bc, 0x1400ffe0 }, + { 0x130003bd, 0x1400ffe0 }, + { 0x130003be, 0x1400ffe0 }, + { 0x130003bf, 0x1400ffe0 }, + { 0x130003c0, 0x1400ffe0 }, + { 0x130003c1, 0x1400ffe0 }, + { 0x130003c2, 0x1400ffe1 }, + { 0x130003c3, 0x1400ffe0 }, + { 0x130003c4, 0x1400ffe0 }, + { 0x130003c5, 0x1400ffe0 }, + { 0x130003c6, 0x1400ffe0 }, + { 0x130003c7, 0x1400ffe0 }, + { 0x130003c8, 0x1400ffe0 }, + { 0x130003c9, 0x1400ffe0 }, + { 0x130003ca, 0x1400ffe0 }, + { 0x130003cb, 0x1400ffe0 }, + { 0x130003cc, 0x1400ffc0 }, + { 0x130003cd, 0x1400ffc1 }, + { 0x130003ce, 0x1400ffc1 }, + { 0x130003d0, 0x1400ffc2 }, + { 0x130003d1, 0x1400ffc7 }, + { 0x138003d2, 0x24000002 }, + { 0x130003d5, 0x1400ffd1 }, + { 0x130003d6, 0x1400ffca }, + { 0x130003d7, 0x14000000 }, + { 0x130003d8, 0x24000001 }, + { 0x130003d9, 0x1400ffff }, + { 0x130003da, 0x24000001 }, + { 0x130003db, 0x1400ffff }, + { 0x130003dc, 0x24000001 }, + { 0x130003dd, 0x1400ffff }, + { 0x130003de, 0x24000001 }, + { 0x130003df, 0x1400ffff }, + { 0x130003e0, 0x24000001 }, + { 0x130003e1, 0x1400ffff }, + { 0x0a0003e2, 0x24000001 }, + { 0x0a0003e3, 0x1400ffff }, + { 0x0a0003e4, 0x24000001 }, + { 0x0a0003e5, 0x1400ffff }, + { 0x0a0003e6, 0x24000001 }, + { 0x0a0003e7, 0x1400ffff }, + { 0x0a0003e8, 0x24000001 }, + { 0x0a0003e9, 0x1400ffff }, + { 0x0a0003ea, 0x24000001 }, + { 0x0a0003eb, 0x1400ffff }, + { 0x0a0003ec, 0x24000001 }, + { 0x0a0003ed, 0x1400ffff }, + { 0x0a0003ee, 0x24000001 }, + { 0x0a0003ef, 0x1400ffff }, + { 0x130003f0, 0x1400ffaa }, + { 0x130003f1, 0x1400ffb0 }, + { 0x130003f2, 0x14000007 }, + { 0x130003f3, 0x14000000 }, + { 0x130003f4, 0x2400ffc4 }, + { 0x130003f5, 0x1400ffa0 }, + { 0x130003f6, 0x64000000 }, + { 0x130003f7, 0x24000001 }, + { 0x130003f8, 0x1400ffff }, + { 0x130003f9, 0x2400fff9 }, + { 0x130003fa, 0x24000001 }, + { 0x130003fb, 0x1400ffff }, + { 0x130003fc, 0x14000000 }, + { 0x138003fd, 0x24000002 }, + { 0x0c000400, 0x24000050 }, + { 0x0c000401, 0x24000050 }, + { 0x0c000402, 0x24000050 }, + { 0x0c000403, 0x24000050 }, + { 0x0c000404, 0x24000050 }, + { 0x0c000405, 0x24000050 }, + { 0x0c000406, 0x24000050 }, + { 0x0c000407, 0x24000050 }, + { 0x0c000408, 0x24000050 }, + { 0x0c000409, 0x24000050 }, + { 0x0c00040a, 0x24000050 }, + { 0x0c00040b, 0x24000050 }, + { 0x0c00040c, 0x24000050 }, + { 0x0c00040d, 0x24000050 }, + { 0x0c00040e, 0x24000050 }, + { 0x0c00040f, 0x24000050 }, + { 0x0c000410, 0x24000020 }, + { 0x0c000411, 0x24000020 }, + { 0x0c000412, 0x24000020 }, + { 0x0c000413, 0x24000020 }, + { 0x0c000414, 0x24000020 }, + { 0x0c000415, 0x24000020 }, + { 0x0c000416, 0x24000020 }, + { 0x0c000417, 0x24000020 }, + { 0x0c000418, 0x24000020 }, + { 0x0c000419, 0x24000020 }, + { 0x0c00041a, 0x24000020 }, + { 0x0c00041b, 0x24000020 }, + { 0x0c00041c, 0x24000020 }, + { 0x0c00041d, 0x24000020 }, + { 0x0c00041e, 0x24000020 }, + { 0x0c00041f, 0x24000020 }, + { 0x0c000420, 0x24000020 }, + { 0x0c000421, 0x24000020 }, + { 0x0c000422, 0x24000020 }, + { 0x0c000423, 0x24000020 }, + { 0x0c000424, 0x24000020 }, + { 0x0c000425, 0x24000020 }, + { 0x0c000426, 0x24000020 }, + { 0x0c000427, 0x24000020 }, + { 0x0c000428, 0x24000020 }, + { 0x0c000429, 0x24000020 }, + { 0x0c00042a, 0x24000020 }, + { 0x0c00042b, 0x24000020 }, + { 0x0c00042c, 0x24000020 }, + { 0x0c00042d, 0x24000020 }, + { 0x0c00042e, 0x24000020 }, + { 0x0c00042f, 0x24000020 }, + { 0x0c000430, 0x1400ffe0 }, + { 0x0c000431, 0x1400ffe0 }, + { 0x0c000432, 0x1400ffe0 }, + { 0x0c000433, 0x1400ffe0 }, + { 0x0c000434, 0x1400ffe0 }, + { 0x0c000435, 0x1400ffe0 }, + { 0x0c000436, 0x1400ffe0 }, + { 0x0c000437, 0x1400ffe0 }, + { 0x0c000438, 0x1400ffe0 }, + { 0x0c000439, 0x1400ffe0 }, + { 0x0c00043a, 0x1400ffe0 }, + { 0x0c00043b, 0x1400ffe0 }, + { 0x0c00043c, 0x1400ffe0 }, + { 0x0c00043d, 0x1400ffe0 }, + { 0x0c00043e, 0x1400ffe0 }, + { 0x0c00043f, 0x1400ffe0 }, + { 0x0c000440, 0x1400ffe0 }, + { 0x0c000441, 0x1400ffe0 }, + { 0x0c000442, 0x1400ffe0 }, + { 0x0c000443, 0x1400ffe0 }, + { 0x0c000444, 0x1400ffe0 }, + { 0x0c000445, 0x1400ffe0 }, + { 0x0c000446, 0x1400ffe0 }, + { 0x0c000447, 0x1400ffe0 }, + { 0x0c000448, 0x1400ffe0 }, + { 0x0c000449, 0x1400ffe0 }, + { 0x0c00044a, 0x1400ffe0 }, + { 0x0c00044b, 0x1400ffe0 }, + { 0x0c00044c, 0x1400ffe0 }, + { 0x0c00044d, 0x1400ffe0 }, + { 0x0c00044e, 0x1400ffe0 }, + { 0x0c00044f, 0x1400ffe0 }, + { 0x0c000450, 0x1400ffb0 }, + { 0x0c000451, 0x1400ffb0 }, + { 0x0c000452, 0x1400ffb0 }, + { 0x0c000453, 0x1400ffb0 }, + { 0x0c000454, 0x1400ffb0 }, + { 0x0c000455, 0x1400ffb0 }, + { 0x0c000456, 0x1400ffb0 }, + { 0x0c000457, 0x1400ffb0 }, + { 0x0c000458, 0x1400ffb0 }, + { 0x0c000459, 0x1400ffb0 }, + { 0x0c00045a, 0x1400ffb0 }, + { 0x0c00045b, 0x1400ffb0 }, + { 0x0c00045c, 0x1400ffb0 }, + { 0x0c00045d, 0x1400ffb0 }, + { 0x0c00045e, 0x1400ffb0 }, + { 0x0c00045f, 0x1400ffb0 }, + { 0x0c000460, 0x24000001 }, + { 0x0c000461, 0x1400ffff }, + { 0x0c000462, 0x24000001 }, + { 0x0c000463, 0x1400ffff }, + { 0x0c000464, 0x24000001 }, + { 0x0c000465, 0x1400ffff }, + { 0x0c000466, 0x24000001 }, + { 0x0c000467, 0x1400ffff }, + { 0x0c000468, 0x24000001 }, + { 0x0c000469, 0x1400ffff }, + { 0x0c00046a, 0x24000001 }, + { 0x0c00046b, 0x1400ffff }, + { 0x0c00046c, 0x24000001 }, + { 0x0c00046d, 0x1400ffff }, + { 0x0c00046e, 0x24000001 }, + { 0x0c00046f, 0x1400ffff }, + { 0x0c000470, 0x24000001 }, + { 0x0c000471, 0x1400ffff }, + { 0x0c000472, 0x24000001 }, + { 0x0c000473, 0x1400ffff }, + { 0x0c000474, 0x24000001 }, + { 0x0c000475, 0x1400ffff }, + { 0x0c000476, 0x24000001 }, + { 0x0c000477, 0x1400ffff }, + { 0x0c000478, 0x24000001 }, + { 0x0c000479, 0x1400ffff }, + { 0x0c00047a, 0x24000001 }, + { 0x0c00047b, 0x1400ffff }, + { 0x0c00047c, 0x24000001 }, + { 0x0c00047d, 0x1400ffff }, + { 0x0c00047e, 0x24000001 }, + { 0x0c00047f, 0x1400ffff }, + { 0x0c000480, 0x24000001 }, + { 0x0c000481, 0x1400ffff }, + { 0x0c000482, 0x68000000 }, + { 0x0c800483, 0x30000003 }, + { 0x0c800488, 0x2c000001 }, + { 0x0c00048a, 0x24000001 }, + { 0x0c00048b, 0x1400ffff }, + { 0x0c00048c, 0x24000001 }, + { 0x0c00048d, 0x1400ffff }, + { 0x0c00048e, 0x24000001 }, + { 0x0c00048f, 0x1400ffff }, + { 0x0c000490, 0x24000001 }, + { 0x0c000491, 0x1400ffff }, + { 0x0c000492, 0x24000001 }, + { 0x0c000493, 0x1400ffff }, + { 0x0c000494, 0x24000001 }, + { 0x0c000495, 0x1400ffff }, + { 0x0c000496, 0x24000001 }, + { 0x0c000497, 0x1400ffff }, + { 0x0c000498, 0x24000001 }, + { 0x0c000499, 0x1400ffff }, + { 0x0c00049a, 0x24000001 }, + { 0x0c00049b, 0x1400ffff }, + { 0x0c00049c, 0x24000001 }, + { 0x0c00049d, 0x1400ffff }, + { 0x0c00049e, 0x24000001 }, + { 0x0c00049f, 0x1400ffff }, + { 0x0c0004a0, 0x24000001 }, + { 0x0c0004a1, 0x1400ffff }, + { 0x0c0004a2, 0x24000001 }, + { 0x0c0004a3, 0x1400ffff }, + { 0x0c0004a4, 0x24000001 }, + { 0x0c0004a5, 0x1400ffff }, + { 0x0c0004a6, 0x24000001 }, + { 0x0c0004a7, 0x1400ffff }, + { 0x0c0004a8, 0x24000001 }, + { 0x0c0004a9, 0x1400ffff }, + { 0x0c0004aa, 0x24000001 }, + { 0x0c0004ab, 0x1400ffff }, + { 0x0c0004ac, 0x24000001 }, + { 0x0c0004ad, 0x1400ffff }, + { 0x0c0004ae, 0x24000001 }, + { 0x0c0004af, 0x1400ffff }, + { 0x0c0004b0, 0x24000001 }, + { 0x0c0004b1, 0x1400ffff }, + { 0x0c0004b2, 0x24000001 }, + { 0x0c0004b3, 0x1400ffff }, + { 0x0c0004b4, 0x24000001 }, + { 0x0c0004b5, 0x1400ffff }, + { 0x0c0004b6, 0x24000001 }, + { 0x0c0004b7, 0x1400ffff }, + { 0x0c0004b8, 0x24000001 }, + { 0x0c0004b9, 0x1400ffff }, + { 0x0c0004ba, 0x24000001 }, + { 0x0c0004bb, 0x1400ffff }, + { 0x0c0004bc, 0x24000001 }, + { 0x0c0004bd, 0x1400ffff }, + { 0x0c0004be, 0x24000001 }, + { 0x0c0004bf, 0x1400ffff }, + { 0x0c0004c0, 0x24000000 }, + { 0x0c0004c1, 0x24000001 }, + { 0x0c0004c2, 0x1400ffff }, + { 0x0c0004c3, 0x24000001 }, + { 0x0c0004c4, 0x1400ffff }, + { 0x0c0004c5, 0x24000001 }, + { 0x0c0004c6, 0x1400ffff }, + { 0x0c0004c7, 0x24000001 }, + { 0x0c0004c8, 0x1400ffff }, + { 0x0c0004c9, 0x24000001 }, + { 0x0c0004ca, 0x1400ffff }, + { 0x0c0004cb, 0x24000001 }, + { 0x0c0004cc, 0x1400ffff }, + { 0x0c0004cd, 0x24000001 }, + { 0x0c0004ce, 0x1400ffff }, + { 0x0c0004d0, 0x24000001 }, + { 0x0c0004d1, 0x1400ffff }, + { 0x0c0004d2, 0x24000001 }, + { 0x0c0004d3, 0x1400ffff }, + { 0x0c0004d4, 0x24000001 }, + { 0x0c0004d5, 0x1400ffff }, + { 0x0c0004d6, 0x24000001 }, + { 0x0c0004d7, 0x1400ffff }, + { 0x0c0004d8, 0x24000001 }, + { 0x0c0004d9, 0x1400ffff }, + { 0x0c0004da, 0x24000001 }, + { 0x0c0004db, 0x1400ffff }, + { 0x0c0004dc, 0x24000001 }, + { 0x0c0004dd, 0x1400ffff }, + { 0x0c0004de, 0x24000001 }, + { 0x0c0004df, 0x1400ffff }, + { 0x0c0004e0, 0x24000001 }, + { 0x0c0004e1, 0x1400ffff }, + { 0x0c0004e2, 0x24000001 }, + { 0x0c0004e3, 0x1400ffff }, + { 0x0c0004e4, 0x24000001 }, + { 0x0c0004e5, 0x1400ffff }, + { 0x0c0004e6, 0x24000001 }, + { 0x0c0004e7, 0x1400ffff }, + { 0x0c0004e8, 0x24000001 }, + { 0x0c0004e9, 0x1400ffff }, + { 0x0c0004ea, 0x24000001 }, + { 0x0c0004eb, 0x1400ffff }, + { 0x0c0004ec, 0x24000001 }, + { 0x0c0004ed, 0x1400ffff }, + { 0x0c0004ee, 0x24000001 }, + { 0x0c0004ef, 0x1400ffff }, + { 0x0c0004f0, 0x24000001 }, + { 0x0c0004f1, 0x1400ffff }, + { 0x0c0004f2, 0x24000001 }, + { 0x0c0004f3, 0x1400ffff }, + { 0x0c0004f4, 0x24000001 }, + { 0x0c0004f5, 0x1400ffff }, + { 0x0c0004f6, 0x24000001 }, + { 0x0c0004f7, 0x1400ffff }, + { 0x0c0004f8, 0x24000001 }, + { 0x0c0004f9, 0x1400ffff }, + { 0x0c000500, 0x24000001 }, + { 0x0c000501, 0x1400ffff }, + { 0x0c000502, 0x24000001 }, + { 0x0c000503, 0x1400ffff }, + { 0x0c000504, 0x24000001 }, + { 0x0c000505, 0x1400ffff }, + { 0x0c000506, 0x24000001 }, + { 0x0c000507, 0x1400ffff }, + { 0x0c000508, 0x24000001 }, + { 0x0c000509, 0x1400ffff }, + { 0x0c00050a, 0x24000001 }, + { 0x0c00050b, 0x1400ffff }, + { 0x0c00050c, 0x24000001 }, + { 0x0c00050d, 0x1400ffff }, + { 0x0c00050e, 0x24000001 }, + { 0x0c00050f, 0x1400ffff }, + { 0x01000531, 0x24000030 }, + { 0x01000532, 0x24000030 }, + { 0x01000533, 0x24000030 }, + { 0x01000534, 0x24000030 }, + { 0x01000535, 0x24000030 }, + { 0x01000536, 0x24000030 }, + { 0x01000537, 0x24000030 }, + { 0x01000538, 0x24000030 }, + { 0x01000539, 0x24000030 }, + { 0x0100053a, 0x24000030 }, + { 0x0100053b, 0x24000030 }, + { 0x0100053c, 0x24000030 }, + { 0x0100053d, 0x24000030 }, + { 0x0100053e, 0x24000030 }, + { 0x0100053f, 0x24000030 }, + { 0x01000540, 0x24000030 }, + { 0x01000541, 0x24000030 }, + { 0x01000542, 0x24000030 }, + { 0x01000543, 0x24000030 }, + { 0x01000544, 0x24000030 }, + { 0x01000545, 0x24000030 }, + { 0x01000546, 0x24000030 }, + { 0x01000547, 0x24000030 }, + { 0x01000548, 0x24000030 }, + { 0x01000549, 0x24000030 }, + { 0x0100054a, 0x24000030 }, + { 0x0100054b, 0x24000030 }, + { 0x0100054c, 0x24000030 }, + { 0x0100054d, 0x24000030 }, + { 0x0100054e, 0x24000030 }, + { 0x0100054f, 0x24000030 }, + { 0x01000550, 0x24000030 }, + { 0x01000551, 0x24000030 }, + { 0x01000552, 0x24000030 }, + { 0x01000553, 0x24000030 }, + { 0x01000554, 0x24000030 }, + { 0x01000555, 0x24000030 }, + { 0x01000556, 0x24000030 }, + { 0x01000559, 0x18000000 }, + { 0x0180055a, 0x54000005 }, + { 0x01000561, 0x1400ffd0 }, + { 0x01000562, 0x1400ffd0 }, + { 0x01000563, 0x1400ffd0 }, + { 0x01000564, 0x1400ffd0 }, + { 0x01000565, 0x1400ffd0 }, + { 0x01000566, 0x1400ffd0 }, + { 0x01000567, 0x1400ffd0 }, + { 0x01000568, 0x1400ffd0 }, + { 0x01000569, 0x1400ffd0 }, + { 0x0100056a, 0x1400ffd0 }, + { 0x0100056b, 0x1400ffd0 }, + { 0x0100056c, 0x1400ffd0 }, + { 0x0100056d, 0x1400ffd0 }, + { 0x0100056e, 0x1400ffd0 }, + { 0x0100056f, 0x1400ffd0 }, + { 0x01000570, 0x1400ffd0 }, + { 0x01000571, 0x1400ffd0 }, + { 0x01000572, 0x1400ffd0 }, + { 0x01000573, 0x1400ffd0 }, + { 0x01000574, 0x1400ffd0 }, + { 0x01000575, 0x1400ffd0 }, + { 0x01000576, 0x1400ffd0 }, + { 0x01000577, 0x1400ffd0 }, + { 0x01000578, 0x1400ffd0 }, + { 0x01000579, 0x1400ffd0 }, + { 0x0100057a, 0x1400ffd0 }, + { 0x0100057b, 0x1400ffd0 }, + { 0x0100057c, 0x1400ffd0 }, + { 0x0100057d, 0x1400ffd0 }, + { 0x0100057e, 0x1400ffd0 }, + { 0x0100057f, 0x1400ffd0 }, + { 0x01000580, 0x1400ffd0 }, + { 0x01000581, 0x1400ffd0 }, + { 0x01000582, 0x1400ffd0 }, + { 0x01000583, 0x1400ffd0 }, + { 0x01000584, 0x1400ffd0 }, + { 0x01000585, 0x1400ffd0 }, + { 0x01000586, 0x1400ffd0 }, + { 0x01000587, 0x14000000 }, + { 0x09000589, 0x54000000 }, + { 0x0100058a, 0x44000000 }, + { 0x19800591, 0x30000028 }, + { 0x198005bb, 0x30000002 }, + { 0x190005be, 0x54000000 }, + { 0x190005bf, 0x30000000 }, + { 0x190005c0, 0x54000000 }, + { 0x198005c1, 0x30000001 }, + { 0x190005c3, 0x54000000 }, + { 0x198005c4, 0x30000001 }, + { 0x190005c6, 0x54000000 }, + { 0x190005c7, 0x30000000 }, + { 0x198005d0, 0x1c00001a }, + { 0x198005f0, 0x1c000002 }, + { 0x198005f3, 0x54000001 }, + { 0x09800600, 0x04000003 }, + { 0x0000060b, 0x5c000000 }, + { 0x0980060c, 0x54000001 }, + { 0x0080060e, 0x68000001 }, + { 0x00800610, 0x30000005 }, + { 0x0900061b, 0x54000000 }, + { 0x0080061e, 0x54000001 }, + { 0x00800621, 0x1c000019 }, + { 0x09000640, 0x18000000 }, + { 0x00800641, 0x1c000009 }, + { 0x1b80064b, 0x30000013 }, + { 0x09800660, 0x34000009 }, + { 0x0080066a, 0x54000003 }, + { 0x0080066e, 0x1c000001 }, + { 0x1b000670, 0x30000000 }, + { 0x00800671, 0x1c000062 }, + { 0x000006d4, 0x54000000 }, + { 0x000006d5, 0x1c000000 }, + { 0x008006d6, 0x30000006 }, + { 0x090006dd, 0x04000000 }, + { 0x000006de, 0x2c000000 }, + { 0x008006df, 0x30000005 }, + { 0x008006e5, 0x18000001 }, + { 0x008006e7, 0x30000001 }, + { 0x000006e9, 0x68000000 }, + { 0x008006ea, 0x30000003 }, + { 0x008006ee, 0x1c000001 }, + { 0x008006f0, 0x34000009 }, + { 0x008006fa, 0x1c000002 }, + { 0x008006fd, 0x68000001 }, + { 0x000006ff, 0x1c000000 }, + { 0x31800700, 0x5400000d }, + { 0x3100070f, 0x04000000 }, + { 0x31000710, 0x1c000000 }, + { 0x31000711, 0x30000000 }, + { 0x31800712, 0x1c00001d }, + { 0x31800730, 0x3000001a }, + { 0x3180074d, 0x1c000020 }, + { 0x37800780, 0x1c000025 }, + { 0x378007a6, 0x3000000a }, + { 0x370007b1, 0x1c000000 }, + { 0x0e800901, 0x30000001 }, + { 0x0e000903, 0x28000000 }, + { 0x0e800904, 0x1c000035 }, + { 0x0e00093c, 0x30000000 }, + { 0x0e00093d, 0x1c000000 }, + { 0x0e80093e, 0x28000002 }, + { 0x0e800941, 0x30000007 }, + { 0x0e800949, 0x28000003 }, + { 0x0e00094d, 0x30000000 }, + { 0x0e000950, 0x1c000000 }, + { 0x0e800951, 0x30000003 }, + { 0x0e800958, 0x1c000009 }, + { 0x0e800962, 0x30000001 }, + { 0x09800964, 0x54000001 }, + { 0x0e800966, 0x34000009 }, + { 0x09000970, 0x54000000 }, + { 0x0e00097d, 0x1c000000 }, + { 0x02000981, 0x30000000 }, + { 0x02800982, 0x28000001 }, + { 0x02800985, 0x1c000007 }, + { 0x0280098f, 0x1c000001 }, + { 0x02800993, 0x1c000015 }, + { 0x028009aa, 0x1c000006 }, + { 0x020009b2, 0x1c000000 }, + { 0x028009b6, 0x1c000003 }, + { 0x020009bc, 0x30000000 }, + { 0x020009bd, 0x1c000000 }, + { 0x028009be, 0x28000002 }, + { 0x028009c1, 0x30000003 }, + { 0x028009c7, 0x28000001 }, + { 0x028009cb, 0x28000001 }, + { 0x020009cd, 0x30000000 }, + { 0x020009ce, 0x1c000000 }, + { 0x020009d7, 0x28000000 }, + { 0x028009dc, 0x1c000001 }, + { 0x028009df, 0x1c000002 }, + { 0x028009e2, 0x30000001 }, + { 0x028009e6, 0x34000009 }, + { 0x028009f0, 0x1c000001 }, + { 0x028009f2, 0x5c000001 }, + { 0x028009f4, 0x3c000005 }, + { 0x020009fa, 0x68000000 }, + { 0x15800a01, 0x30000001 }, + { 0x15000a03, 0x28000000 }, + { 0x15800a05, 0x1c000005 }, + { 0x15800a0f, 0x1c000001 }, + { 0x15800a13, 0x1c000015 }, + { 0x15800a2a, 0x1c000006 }, + { 0x15800a32, 0x1c000001 }, + { 0x15800a35, 0x1c000001 }, + { 0x15800a38, 0x1c000001 }, + { 0x15000a3c, 0x30000000 }, + { 0x15800a3e, 0x28000002 }, + { 0x15800a41, 0x30000001 }, + { 0x15800a47, 0x30000001 }, + { 0x15800a4b, 0x30000002 }, + { 0x15800a59, 0x1c000003 }, + { 0x15000a5e, 0x1c000000 }, + { 0x15800a66, 0x34000009 }, + { 0x15800a70, 0x30000001 }, + { 0x15800a72, 0x1c000002 }, + { 0x14800a81, 0x30000001 }, + { 0x14000a83, 0x28000000 }, + { 0x14800a85, 0x1c000008 }, + { 0x14800a8f, 0x1c000002 }, + { 0x14800a93, 0x1c000015 }, + { 0x14800aaa, 0x1c000006 }, + { 0x14800ab2, 0x1c000001 }, + { 0x14800ab5, 0x1c000004 }, + { 0x14000abc, 0x30000000 }, + { 0x14000abd, 0x1c000000 }, + { 0x14800abe, 0x28000002 }, + { 0x14800ac1, 0x30000004 }, + { 0x14800ac7, 0x30000001 }, + { 0x14000ac9, 0x28000000 }, + { 0x14800acb, 0x28000001 }, + { 0x14000acd, 0x30000000 }, + { 0x14000ad0, 0x1c000000 }, + { 0x14800ae0, 0x1c000001 }, + { 0x14800ae2, 0x30000001 }, + { 0x14800ae6, 0x34000009 }, + { 0x14000af1, 0x5c000000 }, + { 0x2b000b01, 0x30000000 }, + { 0x2b800b02, 0x28000001 }, + { 0x2b800b05, 0x1c000007 }, + { 0x2b800b0f, 0x1c000001 }, + { 0x2b800b13, 0x1c000015 }, + { 0x2b800b2a, 0x1c000006 }, + { 0x2b800b32, 0x1c000001 }, + { 0x2b800b35, 0x1c000004 }, + { 0x2b000b3c, 0x30000000 }, + { 0x2b000b3d, 0x1c000000 }, + { 0x2b000b3e, 0x28000000 }, + { 0x2b000b3f, 0x30000000 }, + { 0x2b000b40, 0x28000000 }, + { 0x2b800b41, 0x30000002 }, + { 0x2b800b47, 0x28000001 }, + { 0x2b800b4b, 0x28000001 }, + { 0x2b000b4d, 0x30000000 }, + { 0x2b000b56, 0x30000000 }, + { 0x2b000b57, 0x28000000 }, + { 0x2b800b5c, 0x1c000001 }, + { 0x2b800b5f, 0x1c000002 }, + { 0x2b800b66, 0x34000009 }, + { 0x2b000b70, 0x68000000 }, + { 0x2b000b71, 0x1c000000 }, + { 0x35000b82, 0x30000000 }, + { 0x35000b83, 0x1c000000 }, + { 0x35800b85, 0x1c000005 }, + { 0x35800b8e, 0x1c000002 }, + { 0x35800b92, 0x1c000003 }, + { 0x35800b99, 0x1c000001 }, + { 0x35000b9c, 0x1c000000 }, + { 0x35800b9e, 0x1c000001 }, + { 0x35800ba3, 0x1c000001 }, + { 0x35800ba8, 0x1c000002 }, + { 0x35800bae, 0x1c00000b }, + { 0x35800bbe, 0x28000001 }, + { 0x35000bc0, 0x30000000 }, + { 0x35800bc1, 0x28000001 }, + { 0x35800bc6, 0x28000002 }, + { 0x35800bca, 0x28000002 }, + { 0x35000bcd, 0x30000000 }, + { 0x35000bd7, 0x28000000 }, + { 0x35800be6, 0x34000009 }, + { 0x35800bf0, 0x3c000002 }, + { 0x35800bf3, 0x68000005 }, + { 0x35000bf9, 0x5c000000 }, + { 0x35000bfa, 0x68000000 }, + { 0x36800c01, 0x28000002 }, + { 0x36800c05, 0x1c000007 }, + { 0x36800c0e, 0x1c000002 }, + { 0x36800c12, 0x1c000016 }, + { 0x36800c2a, 0x1c000009 }, + { 0x36800c35, 0x1c000004 }, + { 0x36800c3e, 0x30000002 }, + { 0x36800c41, 0x28000003 }, + { 0x36800c46, 0x30000002 }, + { 0x36800c4a, 0x30000003 }, + { 0x36800c55, 0x30000001 }, + { 0x36800c60, 0x1c000001 }, + { 0x36800c66, 0x34000009 }, + { 0x1c800c82, 0x28000001 }, + { 0x1c800c85, 0x1c000007 }, + { 0x1c800c8e, 0x1c000002 }, + { 0x1c800c92, 0x1c000016 }, + { 0x1c800caa, 0x1c000009 }, + { 0x1c800cb5, 0x1c000004 }, + { 0x1c000cbc, 0x30000000 }, + { 0x1c000cbd, 0x1c000000 }, + { 0x1c000cbe, 0x28000000 }, + { 0x1c000cbf, 0x30000000 }, + { 0x1c800cc0, 0x28000004 }, + { 0x1c000cc6, 0x30000000 }, + { 0x1c800cc7, 0x28000001 }, + { 0x1c800cca, 0x28000001 }, + { 0x1c800ccc, 0x30000001 }, + { 0x1c800cd5, 0x28000001 }, + { 0x1c000cde, 0x1c000000 }, + { 0x1c800ce0, 0x1c000001 }, + { 0x1c800ce6, 0x34000009 }, + { 0x24800d02, 0x28000001 }, + { 0x24800d05, 0x1c000007 }, + { 0x24800d0e, 0x1c000002 }, + { 0x24800d12, 0x1c000016 }, + { 0x24800d2a, 0x1c00000f }, + { 0x24800d3e, 0x28000002 }, + { 0x24800d41, 0x30000002 }, + { 0x24800d46, 0x28000002 }, + { 0x24800d4a, 0x28000002 }, + { 0x24000d4d, 0x30000000 }, + { 0x24000d57, 0x28000000 }, + { 0x24800d60, 0x1c000001 }, + { 0x24800d66, 0x34000009 }, + { 0x2f800d82, 0x28000001 }, + { 0x2f800d85, 0x1c000011 }, + { 0x2f800d9a, 0x1c000017 }, + { 0x2f800db3, 0x1c000008 }, + { 0x2f000dbd, 0x1c000000 }, + { 0x2f800dc0, 0x1c000006 }, + { 0x2f000dca, 0x30000000 }, + { 0x2f800dcf, 0x28000002 }, + { 0x2f800dd2, 0x30000002 }, + { 0x2f000dd6, 0x30000000 }, + { 0x2f800dd8, 0x28000007 }, + { 0x2f800df2, 0x28000001 }, + { 0x2f000df4, 0x54000000 }, + { 0x38800e01, 0x1c00002f }, + { 0x38000e31, 0x30000000 }, + { 0x38800e32, 0x1c000001 }, + { 0x38800e34, 0x30000006 }, + { 0x09000e3f, 0x5c000000 }, + { 0x38800e40, 0x1c000005 }, + { 0x38000e46, 0x18000000 }, + { 0x38800e47, 0x30000007 }, + { 0x38000e4f, 0x54000000 }, + { 0x38800e50, 0x34000009 }, + { 0x38800e5a, 0x54000001 }, + { 0x20800e81, 0x1c000001 }, + { 0x20000e84, 0x1c000000 }, + { 0x20800e87, 0x1c000001 }, + { 0x20000e8a, 0x1c000000 }, + { 0x20000e8d, 0x1c000000 }, + { 0x20800e94, 0x1c000003 }, + { 0x20800e99, 0x1c000006 }, + { 0x20800ea1, 0x1c000002 }, + { 0x20000ea5, 0x1c000000 }, + { 0x20000ea7, 0x1c000000 }, + { 0x20800eaa, 0x1c000001 }, + { 0x20800ead, 0x1c000003 }, + { 0x20000eb1, 0x30000000 }, + { 0x20800eb2, 0x1c000001 }, + { 0x20800eb4, 0x30000005 }, + { 0x20800ebb, 0x30000001 }, + { 0x20000ebd, 0x1c000000 }, + { 0x20800ec0, 0x1c000004 }, + { 0x20000ec6, 0x18000000 }, + { 0x20800ec8, 0x30000005 }, + { 0x20800ed0, 0x34000009 }, + { 0x20800edc, 0x1c000001 }, + { 0x39000f00, 0x1c000000 }, + { 0x39800f01, 0x68000002 }, + { 0x39800f04, 0x5400000e }, + { 0x39800f13, 0x68000004 }, + { 0x39800f18, 0x30000001 }, + { 0x39800f1a, 0x68000005 }, + { 0x39800f20, 0x34000009 }, + { 0x39800f2a, 0x3c000009 }, + { 0x39000f34, 0x68000000 }, + { 0x39000f35, 0x30000000 }, + { 0x39000f36, 0x68000000 }, + { 0x39000f37, 0x30000000 }, + { 0x39000f38, 0x68000000 }, + { 0x39000f39, 0x30000000 }, + { 0x39000f3a, 0x58000000 }, + { 0x39000f3b, 0x48000000 }, + { 0x39000f3c, 0x58000000 }, + { 0x39000f3d, 0x48000000 }, + { 0x39800f3e, 0x28000001 }, + { 0x39800f40, 0x1c000007 }, + { 0x39800f49, 0x1c000021 }, + { 0x39800f71, 0x3000000d }, + { 0x39000f7f, 0x28000000 }, + { 0x39800f80, 0x30000004 }, + { 0x39000f85, 0x54000000 }, + { 0x39800f86, 0x30000001 }, + { 0x39800f88, 0x1c000003 }, + { 0x39800f90, 0x30000007 }, + { 0x39800f99, 0x30000023 }, + { 0x39800fbe, 0x68000007 }, + { 0x39000fc6, 0x30000000 }, + { 0x39800fc7, 0x68000005 }, + { 0x39000fcf, 0x68000000 }, + { 0x39800fd0, 0x54000001 }, + { 0x26801000, 0x1c000021 }, + { 0x26801023, 0x1c000004 }, + { 0x26801029, 0x1c000001 }, + { 0x2600102c, 0x28000000 }, + { 0x2680102d, 0x30000003 }, + { 0x26001031, 0x28000000 }, + { 0x26001032, 0x30000000 }, + { 0x26801036, 0x30000001 }, + { 0x26001038, 0x28000000 }, + { 0x26001039, 0x30000000 }, + { 0x26801040, 0x34000009 }, + { 0x2680104a, 0x54000005 }, + { 0x26801050, 0x1c000005 }, + { 0x26801056, 0x28000001 }, + { 0x26801058, 0x30000001 }, + { 0x100010a0, 0x24001c60 }, + { 0x100010a1, 0x24001c60 }, + { 0x100010a2, 0x24001c60 }, + { 0x100010a3, 0x24001c60 }, + { 0x100010a4, 0x24001c60 }, + { 0x100010a5, 0x24001c60 }, + { 0x100010a6, 0x24001c60 }, + { 0x100010a7, 0x24001c60 }, + { 0x100010a8, 0x24001c60 }, + { 0x100010a9, 0x24001c60 }, + { 0x100010aa, 0x24001c60 }, + { 0x100010ab, 0x24001c60 }, + { 0x100010ac, 0x24001c60 }, + { 0x100010ad, 0x24001c60 }, + { 0x100010ae, 0x24001c60 }, + { 0x100010af, 0x24001c60 }, + { 0x100010b0, 0x24001c60 }, + { 0x100010b1, 0x24001c60 }, + { 0x100010b2, 0x24001c60 }, + { 0x100010b3, 0x24001c60 }, + { 0x100010b4, 0x24001c60 }, + { 0x100010b5, 0x24001c60 }, + { 0x100010b6, 0x24001c60 }, + { 0x100010b7, 0x24001c60 }, + { 0x100010b8, 0x24001c60 }, + { 0x100010b9, 0x24001c60 }, + { 0x100010ba, 0x24001c60 }, + { 0x100010bb, 0x24001c60 }, + { 0x100010bc, 0x24001c60 }, + { 0x100010bd, 0x24001c60 }, + { 0x100010be, 0x24001c60 }, + { 0x100010bf, 0x24001c60 }, + { 0x100010c0, 0x24001c60 }, + { 0x100010c1, 0x24001c60 }, + { 0x100010c2, 0x24001c60 }, + { 0x100010c3, 0x24001c60 }, + { 0x100010c4, 0x24001c60 }, + { 0x100010c5, 0x24001c60 }, + { 0x108010d0, 0x1c00002a }, + { 0x090010fb, 0x54000000 }, + { 0x100010fc, 0x18000000 }, + { 0x17801100, 0x1c000059 }, + { 0x1780115f, 0x1c000043 }, + { 0x178011a8, 0x1c000051 }, + { 0x0f801200, 0x1c000048 }, + { 0x0f80124a, 0x1c000003 }, + { 0x0f801250, 0x1c000006 }, + { 0x0f001258, 0x1c000000 }, + { 0x0f80125a, 0x1c000003 }, + { 0x0f801260, 0x1c000028 }, + { 0x0f80128a, 0x1c000003 }, + { 0x0f801290, 0x1c000020 }, + { 0x0f8012b2, 0x1c000003 }, + { 0x0f8012b8, 0x1c000006 }, + { 0x0f0012c0, 0x1c000000 }, + { 0x0f8012c2, 0x1c000003 }, + { 0x0f8012c8, 0x1c00000e }, + { 0x0f8012d8, 0x1c000038 }, + { 0x0f801312, 0x1c000003 }, + { 0x0f801318, 0x1c000042 }, + { 0x0f00135f, 0x30000000 }, + { 0x0f001360, 0x68000000 }, + { 0x0f801361, 0x54000007 }, + { 0x0f801369, 0x3c000013 }, + { 0x0f801380, 0x1c00000f }, + { 0x0f801390, 0x68000009 }, + { 0x088013a0, 0x1c000054 }, + { 0x07801401, 0x1c00026b }, + { 0x0780166d, 0x54000001 }, + { 0x0780166f, 0x1c000007 }, + { 0x28001680, 0x74000000 }, + { 0x28801681, 0x1c000019 }, + { 0x2800169b, 0x58000000 }, + { 0x2800169c, 0x48000000 }, + { 0x2d8016a0, 0x1c00004a }, + { 0x098016eb, 0x54000002 }, + { 0x2d8016ee, 0x38000002 }, + { 0x32801700, 0x1c00000c }, + { 0x3280170e, 0x1c000003 }, + { 0x32801712, 0x30000002 }, + { 0x18801720, 0x1c000011 }, + { 0x18801732, 0x30000002 }, + { 0x09801735, 0x54000001 }, + { 0x06801740, 0x1c000011 }, + { 0x06801752, 0x30000001 }, + { 0x33801760, 0x1c00000c }, + { 0x3380176e, 0x1c000002 }, + { 0x33801772, 0x30000001 }, + { 0x1f801780, 0x1c000033 }, + { 0x1f8017b4, 0x04000001 }, + { 0x1f0017b6, 0x28000000 }, + { 0x1f8017b7, 0x30000006 }, + { 0x1f8017be, 0x28000007 }, + { 0x1f0017c6, 0x30000000 }, + { 0x1f8017c7, 0x28000001 }, + { 0x1f8017c9, 0x3000000a }, + { 0x1f8017d4, 0x54000002 }, + { 0x1f0017d7, 0x18000000 }, + { 0x1f8017d8, 0x54000002 }, + { 0x1f0017db, 0x5c000000 }, + { 0x1f0017dc, 0x1c000000 }, + { 0x1f0017dd, 0x30000000 }, + { 0x1f8017e0, 0x34000009 }, + { 0x1f8017f0, 0x3c000009 }, + { 0x25801800, 0x54000005 }, + { 0x25001806, 0x44000000 }, + { 0x25801807, 0x54000003 }, + { 0x2580180b, 0x30000002 }, + { 0x2500180e, 0x74000000 }, + { 0x25801810, 0x34000009 }, + { 0x25801820, 0x1c000022 }, + { 0x25001843, 0x18000000 }, + { 0x25801844, 0x1c000033 }, + { 0x25801880, 0x1c000028 }, + { 0x250018a9, 0x30000000 }, + { 0x22801900, 0x1c00001c }, + { 0x22801920, 0x30000002 }, + { 0x22801923, 0x28000003 }, + { 0x22801927, 0x30000001 }, + { 0x22801929, 0x28000002 }, + { 0x22801930, 0x28000001 }, + { 0x22001932, 0x30000000 }, + { 0x22801933, 0x28000005 }, + { 0x22801939, 0x30000002 }, + { 0x22001940, 0x68000000 }, + { 0x22801944, 0x54000001 }, + { 0x22801946, 0x34000009 }, + { 0x34801950, 0x1c00001d }, + { 0x34801970, 0x1c000004 }, + { 0x27801980, 0x1c000029 }, + { 0x278019b0, 0x28000010 }, + { 0x278019c1, 0x1c000006 }, + { 0x278019c8, 0x28000001 }, + { 0x278019d0, 0x34000009 }, + { 0x278019de, 0x54000001 }, + { 0x1f8019e0, 0x6800001f }, + { 0x05801a00, 0x1c000016 }, + { 0x05801a17, 0x30000001 }, + { 0x05801a19, 0x28000002 }, + { 0x05801a1e, 0x54000001 }, + { 0x21801d00, 0x1400002b }, + { 0x21801d2c, 0x18000035 }, + { 0x21801d62, 0x14000015 }, + { 0x0c001d78, 0x18000000 }, + { 0x21801d79, 0x14000021 }, + { 0x21801d9b, 0x18000024 }, + { 0x1b801dc0, 0x30000003 }, + { 0x21001e00, 0x24000001 }, + { 0x21001e01, 0x1400ffff }, + { 0x21001e02, 0x24000001 }, + { 0x21001e03, 0x1400ffff }, + { 0x21001e04, 0x24000001 }, + { 0x21001e05, 0x1400ffff }, + { 0x21001e06, 0x24000001 }, + { 0x21001e07, 0x1400ffff }, + { 0x21001e08, 0x24000001 }, + { 0x21001e09, 0x1400ffff }, + { 0x21001e0a, 0x24000001 }, + { 0x21001e0b, 0x1400ffff }, + { 0x21001e0c, 0x24000001 }, + { 0x21001e0d, 0x1400ffff }, + { 0x21001e0e, 0x24000001 }, + { 0x21001e0f, 0x1400ffff }, + { 0x21001e10, 0x24000001 }, + { 0x21001e11, 0x1400ffff }, + { 0x21001e12, 0x24000001 }, + { 0x21001e13, 0x1400ffff }, + { 0x21001e14, 0x24000001 }, + { 0x21001e15, 0x1400ffff }, + { 0x21001e16, 0x24000001 }, + { 0x21001e17, 0x1400ffff }, + { 0x21001e18, 0x24000001 }, + { 0x21001e19, 0x1400ffff }, + { 0x21001e1a, 0x24000001 }, + { 0x21001e1b, 0x1400ffff }, + { 0x21001e1c, 0x24000001 }, + { 0x21001e1d, 0x1400ffff }, + { 0x21001e1e, 0x24000001 }, + { 0x21001e1f, 0x1400ffff }, + { 0x21001e20, 0x24000001 }, + { 0x21001e21, 0x1400ffff }, + { 0x21001e22, 0x24000001 }, + { 0x21001e23, 0x1400ffff }, + { 0x21001e24, 0x24000001 }, + { 0x21001e25, 0x1400ffff }, + { 0x21001e26, 0x24000001 }, + { 0x21001e27, 0x1400ffff }, + { 0x21001e28, 0x24000001 }, + { 0x21001e29, 0x1400ffff }, + { 0x21001e2a, 0x24000001 }, + { 0x21001e2b, 0x1400ffff }, + { 0x21001e2c, 0x24000001 }, + { 0x21001e2d, 0x1400ffff }, + { 0x21001e2e, 0x24000001 }, + { 0x21001e2f, 0x1400ffff }, + { 0x21001e30, 0x24000001 }, + { 0x21001e31, 0x1400ffff }, + { 0x21001e32, 0x24000001 }, + { 0x21001e33, 0x1400ffff }, + { 0x21001e34, 0x24000001 }, + { 0x21001e35, 0x1400ffff }, + { 0x21001e36, 0x24000001 }, + { 0x21001e37, 0x1400ffff }, + { 0x21001e38, 0x24000001 }, + { 0x21001e39, 0x1400ffff }, + { 0x21001e3a, 0x24000001 }, + { 0x21001e3b, 0x1400ffff }, + { 0x21001e3c, 0x24000001 }, + { 0x21001e3d, 0x1400ffff }, + { 0x21001e3e, 0x24000001 }, + { 0x21001e3f, 0x1400ffff }, + { 0x21001e40, 0x24000001 }, + { 0x21001e41, 0x1400ffff }, + { 0x21001e42, 0x24000001 }, + { 0x21001e43, 0x1400ffff }, + { 0x21001e44, 0x24000001 }, + { 0x21001e45, 0x1400ffff }, + { 0x21001e46, 0x24000001 }, + { 0x21001e47, 0x1400ffff }, + { 0x21001e48, 0x24000001 }, + { 0x21001e49, 0x1400ffff }, + { 0x21001e4a, 0x24000001 }, + { 0x21001e4b, 0x1400ffff }, + { 0x21001e4c, 0x24000001 }, + { 0x21001e4d, 0x1400ffff }, + { 0x21001e4e, 0x24000001 }, + { 0x21001e4f, 0x1400ffff }, + { 0x21001e50, 0x24000001 }, + { 0x21001e51, 0x1400ffff }, + { 0x21001e52, 0x24000001 }, + { 0x21001e53, 0x1400ffff }, + { 0x21001e54, 0x24000001 }, + { 0x21001e55, 0x1400ffff }, + { 0x21001e56, 0x24000001 }, + { 0x21001e57, 0x1400ffff }, + { 0x21001e58, 0x24000001 }, + { 0x21001e59, 0x1400ffff }, + { 0x21001e5a, 0x24000001 }, + { 0x21001e5b, 0x1400ffff }, + { 0x21001e5c, 0x24000001 }, + { 0x21001e5d, 0x1400ffff }, + { 0x21001e5e, 0x24000001 }, + { 0x21001e5f, 0x1400ffff }, + { 0x21001e60, 0x24000001 }, + { 0x21001e61, 0x1400ffff }, + { 0x21001e62, 0x24000001 }, + { 0x21001e63, 0x1400ffff }, + { 0x21001e64, 0x24000001 }, + { 0x21001e65, 0x1400ffff }, + { 0x21001e66, 0x24000001 }, + { 0x21001e67, 0x1400ffff }, + { 0x21001e68, 0x24000001 }, + { 0x21001e69, 0x1400ffff }, + { 0x21001e6a, 0x24000001 }, + { 0x21001e6b, 0x1400ffff }, + { 0x21001e6c, 0x24000001 }, + { 0x21001e6d, 0x1400ffff }, + { 0x21001e6e, 0x24000001 }, + { 0x21001e6f, 0x1400ffff }, + { 0x21001e70, 0x24000001 }, + { 0x21001e71, 0x1400ffff }, + { 0x21001e72, 0x24000001 }, + { 0x21001e73, 0x1400ffff }, + { 0x21001e74, 0x24000001 }, + { 0x21001e75, 0x1400ffff }, + { 0x21001e76, 0x24000001 }, + { 0x21001e77, 0x1400ffff }, + { 0x21001e78, 0x24000001 }, + { 0x21001e79, 0x1400ffff }, + { 0x21001e7a, 0x24000001 }, + { 0x21001e7b, 0x1400ffff }, + { 0x21001e7c, 0x24000001 }, + { 0x21001e7d, 0x1400ffff }, + { 0x21001e7e, 0x24000001 }, + { 0x21001e7f, 0x1400ffff }, + { 0x21001e80, 0x24000001 }, + { 0x21001e81, 0x1400ffff }, + { 0x21001e82, 0x24000001 }, + { 0x21001e83, 0x1400ffff }, + { 0x21001e84, 0x24000001 }, + { 0x21001e85, 0x1400ffff }, + { 0x21001e86, 0x24000001 }, + { 0x21001e87, 0x1400ffff }, + { 0x21001e88, 0x24000001 }, + { 0x21001e89, 0x1400ffff }, + { 0x21001e8a, 0x24000001 }, + { 0x21001e8b, 0x1400ffff }, + { 0x21001e8c, 0x24000001 }, + { 0x21001e8d, 0x1400ffff }, + { 0x21001e8e, 0x24000001 }, + { 0x21001e8f, 0x1400ffff }, + { 0x21001e90, 0x24000001 }, + { 0x21001e91, 0x1400ffff }, + { 0x21001e92, 0x24000001 }, + { 0x21001e93, 0x1400ffff }, + { 0x21001e94, 0x24000001 }, + { 0x21001e95, 0x1400ffff }, + { 0x21801e96, 0x14000004 }, + { 0x21001e9b, 0x1400ffc5 }, + { 0x21001ea0, 0x24000001 }, + { 0x21001ea1, 0x1400ffff }, + { 0x21001ea2, 0x24000001 }, + { 0x21001ea3, 0x1400ffff }, + { 0x21001ea4, 0x24000001 }, + { 0x21001ea5, 0x1400ffff }, + { 0x21001ea6, 0x24000001 }, + { 0x21001ea7, 0x1400ffff }, + { 0x21001ea8, 0x24000001 }, + { 0x21001ea9, 0x1400ffff }, + { 0x21001eaa, 0x24000001 }, + { 0x21001eab, 0x1400ffff }, + { 0x21001eac, 0x24000001 }, + { 0x21001ead, 0x1400ffff }, + { 0x21001eae, 0x24000001 }, + { 0x21001eaf, 0x1400ffff }, + { 0x21001eb0, 0x24000001 }, + { 0x21001eb1, 0x1400ffff }, + { 0x21001eb2, 0x24000001 }, + { 0x21001eb3, 0x1400ffff }, + { 0x21001eb4, 0x24000001 }, + { 0x21001eb5, 0x1400ffff }, + { 0x21001eb6, 0x24000001 }, + { 0x21001eb7, 0x1400ffff }, + { 0x21001eb8, 0x24000001 }, + { 0x21001eb9, 0x1400ffff }, + { 0x21001eba, 0x24000001 }, + { 0x21001ebb, 0x1400ffff }, + { 0x21001ebc, 0x24000001 }, + { 0x21001ebd, 0x1400ffff }, + { 0x21001ebe, 0x24000001 }, + { 0x21001ebf, 0x1400ffff }, + { 0x21001ec0, 0x24000001 }, + { 0x21001ec1, 0x1400ffff }, + { 0x21001ec2, 0x24000001 }, + { 0x21001ec3, 0x1400ffff }, + { 0x21001ec4, 0x24000001 }, + { 0x21001ec5, 0x1400ffff }, + { 0x21001ec6, 0x24000001 }, + { 0x21001ec7, 0x1400ffff }, + { 0x21001ec8, 0x24000001 }, + { 0x21001ec9, 0x1400ffff }, + { 0x21001eca, 0x24000001 }, + { 0x21001ecb, 0x1400ffff }, + { 0x21001ecc, 0x24000001 }, + { 0x21001ecd, 0x1400ffff }, + { 0x21001ece, 0x24000001 }, + { 0x21001ecf, 0x1400ffff }, + { 0x21001ed0, 0x24000001 }, + { 0x21001ed1, 0x1400ffff }, + { 0x21001ed2, 0x24000001 }, + { 0x21001ed3, 0x1400ffff }, + { 0x21001ed4, 0x24000001 }, + { 0x21001ed5, 0x1400ffff }, + { 0x21001ed6, 0x24000001 }, + { 0x21001ed7, 0x1400ffff }, + { 0x21001ed8, 0x24000001 }, + { 0x21001ed9, 0x1400ffff }, + { 0x21001eda, 0x24000001 }, + { 0x21001edb, 0x1400ffff }, + { 0x21001edc, 0x24000001 }, + { 0x21001edd, 0x1400ffff }, + { 0x21001ede, 0x24000001 }, + { 0x21001edf, 0x1400ffff }, + { 0x21001ee0, 0x24000001 }, + { 0x21001ee1, 0x1400ffff }, + { 0x21001ee2, 0x24000001 }, + { 0x21001ee3, 0x1400ffff }, + { 0x21001ee4, 0x24000001 }, + { 0x21001ee5, 0x1400ffff }, + { 0x21001ee6, 0x24000001 }, + { 0x21001ee7, 0x1400ffff }, + { 0x21001ee8, 0x24000001 }, + { 0x21001ee9, 0x1400ffff }, + { 0x21001eea, 0x24000001 }, + { 0x21001eeb, 0x1400ffff }, + { 0x21001eec, 0x24000001 }, + { 0x21001eed, 0x1400ffff }, + { 0x21001eee, 0x24000001 }, + { 0x21001eef, 0x1400ffff }, + { 0x21001ef0, 0x24000001 }, + { 0x21001ef1, 0x1400ffff }, + { 0x21001ef2, 0x24000001 }, + { 0x21001ef3, 0x1400ffff }, + { 0x21001ef4, 0x24000001 }, + { 0x21001ef5, 0x1400ffff }, + { 0x21001ef6, 0x24000001 }, + { 0x21001ef7, 0x1400ffff }, + { 0x21001ef8, 0x24000001 }, + { 0x21001ef9, 0x1400ffff }, + { 0x13001f00, 0x14000008 }, + { 0x13001f01, 0x14000008 }, + { 0x13001f02, 0x14000008 }, + { 0x13001f03, 0x14000008 }, + { 0x13001f04, 0x14000008 }, + { 0x13001f05, 0x14000008 }, + { 0x13001f06, 0x14000008 }, + { 0x13001f07, 0x14000008 }, + { 0x13001f08, 0x2400fff8 }, + { 0x13001f09, 0x2400fff8 }, + { 0x13001f0a, 0x2400fff8 }, + { 0x13001f0b, 0x2400fff8 }, + { 0x13001f0c, 0x2400fff8 }, + { 0x13001f0d, 0x2400fff8 }, + { 0x13001f0e, 0x2400fff8 }, + { 0x13001f0f, 0x2400fff8 }, + { 0x13001f10, 0x14000008 }, + { 0x13001f11, 0x14000008 }, + { 0x13001f12, 0x14000008 }, + { 0x13001f13, 0x14000008 }, + { 0x13001f14, 0x14000008 }, + { 0x13001f15, 0x14000008 }, + { 0x13001f18, 0x2400fff8 }, + { 0x13001f19, 0x2400fff8 }, + { 0x13001f1a, 0x2400fff8 }, + { 0x13001f1b, 0x2400fff8 }, + { 0x13001f1c, 0x2400fff8 }, + { 0x13001f1d, 0x2400fff8 }, + { 0x13001f20, 0x14000008 }, + { 0x13001f21, 0x14000008 }, + { 0x13001f22, 0x14000008 }, + { 0x13001f23, 0x14000008 }, + { 0x13001f24, 0x14000008 }, + { 0x13001f25, 0x14000008 }, + { 0x13001f26, 0x14000008 }, + { 0x13001f27, 0x14000008 }, + { 0x13001f28, 0x2400fff8 }, + { 0x13001f29, 0x2400fff8 }, + { 0x13001f2a, 0x2400fff8 }, + { 0x13001f2b, 0x2400fff8 }, + { 0x13001f2c, 0x2400fff8 }, + { 0x13001f2d, 0x2400fff8 }, + { 0x13001f2e, 0x2400fff8 }, + { 0x13001f2f, 0x2400fff8 }, + { 0x13001f30, 0x14000008 }, + { 0x13001f31, 0x14000008 }, + { 0x13001f32, 0x14000008 }, + { 0x13001f33, 0x14000008 }, + { 0x13001f34, 0x14000008 }, + { 0x13001f35, 0x14000008 }, + { 0x13001f36, 0x14000008 }, + { 0x13001f37, 0x14000008 }, + { 0x13001f38, 0x2400fff8 }, + { 0x13001f39, 0x2400fff8 }, + { 0x13001f3a, 0x2400fff8 }, + { 0x13001f3b, 0x2400fff8 }, + { 0x13001f3c, 0x2400fff8 }, + { 0x13001f3d, 0x2400fff8 }, + { 0x13001f3e, 0x2400fff8 }, + { 0x13001f3f, 0x2400fff8 }, + { 0x13001f40, 0x14000008 }, + { 0x13001f41, 0x14000008 }, + { 0x13001f42, 0x14000008 }, + { 0x13001f43, 0x14000008 }, + { 0x13001f44, 0x14000008 }, + { 0x13001f45, 0x14000008 }, + { 0x13001f48, 0x2400fff8 }, + { 0x13001f49, 0x2400fff8 }, + { 0x13001f4a, 0x2400fff8 }, + { 0x13001f4b, 0x2400fff8 }, + { 0x13001f4c, 0x2400fff8 }, + { 0x13001f4d, 0x2400fff8 }, + { 0x13001f50, 0x14000000 }, + { 0x13001f51, 0x14000008 }, + { 0x13001f52, 0x14000000 }, + { 0x13001f53, 0x14000008 }, + { 0x13001f54, 0x14000000 }, + { 0x13001f55, 0x14000008 }, + { 0x13001f56, 0x14000000 }, + { 0x13001f57, 0x14000008 }, + { 0x13001f59, 0x2400fff8 }, + { 0x13001f5b, 0x2400fff8 }, + { 0x13001f5d, 0x2400fff8 }, + { 0x13001f5f, 0x2400fff8 }, + { 0x13001f60, 0x14000008 }, + { 0x13001f61, 0x14000008 }, + { 0x13001f62, 0x14000008 }, + { 0x13001f63, 0x14000008 }, + { 0x13001f64, 0x14000008 }, + { 0x13001f65, 0x14000008 }, + { 0x13001f66, 0x14000008 }, + { 0x13001f67, 0x14000008 }, + { 0x13001f68, 0x2400fff8 }, + { 0x13001f69, 0x2400fff8 }, + { 0x13001f6a, 0x2400fff8 }, + { 0x13001f6b, 0x2400fff8 }, + { 0x13001f6c, 0x2400fff8 }, + { 0x13001f6d, 0x2400fff8 }, + { 0x13001f6e, 0x2400fff8 }, + { 0x13001f6f, 0x2400fff8 }, + { 0x13001f70, 0x1400004a }, + { 0x13001f71, 0x1400004a }, + { 0x13001f72, 0x14000056 }, + { 0x13001f73, 0x14000056 }, + { 0x13001f74, 0x14000056 }, + { 0x13001f75, 0x14000056 }, + { 0x13001f76, 0x14000064 }, + { 0x13001f77, 0x14000064 }, + { 0x13001f78, 0x14000080 }, + { 0x13001f79, 0x14000080 }, + { 0x13001f7a, 0x14000070 }, + { 0x13001f7b, 0x14000070 }, + { 0x13001f7c, 0x1400007e }, + { 0x13001f7d, 0x1400007e }, + { 0x13001f80, 0x14000008 }, + { 0x13001f81, 0x14000008 }, + { 0x13001f82, 0x14000008 }, + { 0x13001f83, 0x14000008 }, + { 0x13001f84, 0x14000008 }, + { 0x13001f85, 0x14000008 }, + { 0x13001f86, 0x14000008 }, + { 0x13001f87, 0x14000008 }, + { 0x13001f88, 0x2000fff8 }, + { 0x13001f89, 0x2000fff8 }, + { 0x13001f8a, 0x2000fff8 }, + { 0x13001f8b, 0x2000fff8 }, + { 0x13001f8c, 0x2000fff8 }, + { 0x13001f8d, 0x2000fff8 }, + { 0x13001f8e, 0x2000fff8 }, + { 0x13001f8f, 0x2000fff8 }, + { 0x13001f90, 0x14000008 }, + { 0x13001f91, 0x14000008 }, + { 0x13001f92, 0x14000008 }, + { 0x13001f93, 0x14000008 }, + { 0x13001f94, 0x14000008 }, + { 0x13001f95, 0x14000008 }, + { 0x13001f96, 0x14000008 }, + { 0x13001f97, 0x14000008 }, + { 0x13001f98, 0x2000fff8 }, + { 0x13001f99, 0x2000fff8 }, + { 0x13001f9a, 0x2000fff8 }, + { 0x13001f9b, 0x2000fff8 }, + { 0x13001f9c, 0x2000fff8 }, + { 0x13001f9d, 0x2000fff8 }, + { 0x13001f9e, 0x2000fff8 }, + { 0x13001f9f, 0x2000fff8 }, + { 0x13001fa0, 0x14000008 }, + { 0x13001fa1, 0x14000008 }, + { 0x13001fa2, 0x14000008 }, + { 0x13001fa3, 0x14000008 }, + { 0x13001fa4, 0x14000008 }, + { 0x13001fa5, 0x14000008 }, + { 0x13001fa6, 0x14000008 }, + { 0x13001fa7, 0x14000008 }, + { 0x13001fa8, 0x2000fff8 }, + { 0x13001fa9, 0x2000fff8 }, + { 0x13001faa, 0x2000fff8 }, + { 0x13001fab, 0x2000fff8 }, + { 0x13001fac, 0x2000fff8 }, + { 0x13001fad, 0x2000fff8 }, + { 0x13001fae, 0x2000fff8 }, + { 0x13001faf, 0x2000fff8 }, + { 0x13001fb0, 0x14000008 }, + { 0x13001fb1, 0x14000008 }, + { 0x13001fb2, 0x14000000 }, + { 0x13001fb3, 0x14000009 }, + { 0x13001fb4, 0x14000000 }, + { 0x13801fb6, 0x14000001 }, + { 0x13001fb8, 0x2400fff8 }, + { 0x13001fb9, 0x2400fff8 }, + { 0x13001fba, 0x2400ffb6 }, + { 0x13001fbb, 0x2400ffb6 }, + { 0x13001fbc, 0x2000fff7 }, + { 0x13001fbd, 0x60000000 }, + { 0x13001fbe, 0x1400e3db }, + { 0x13801fbf, 0x60000002 }, + { 0x13001fc2, 0x14000000 }, + { 0x13001fc3, 0x14000009 }, + { 0x13001fc4, 0x14000000 }, + { 0x13801fc6, 0x14000001 }, + { 0x13001fc8, 0x2400ffaa }, + { 0x13001fc9, 0x2400ffaa }, + { 0x13001fca, 0x2400ffaa }, + { 0x13001fcb, 0x2400ffaa }, + { 0x13001fcc, 0x2000fff7 }, + { 0x13801fcd, 0x60000002 }, + { 0x13001fd0, 0x14000008 }, + { 0x13001fd1, 0x14000008 }, + { 0x13801fd2, 0x14000001 }, + { 0x13801fd6, 0x14000001 }, + { 0x13001fd8, 0x2400fff8 }, + { 0x13001fd9, 0x2400fff8 }, + { 0x13001fda, 0x2400ff9c }, + { 0x13001fdb, 0x2400ff9c }, + { 0x13801fdd, 0x60000002 }, + { 0x13001fe0, 0x14000008 }, + { 0x13001fe1, 0x14000008 }, + { 0x13801fe2, 0x14000002 }, + { 0x13001fe5, 0x14000007 }, + { 0x13801fe6, 0x14000001 }, + { 0x13001fe8, 0x2400fff8 }, + { 0x13001fe9, 0x2400fff8 }, + { 0x13001fea, 0x2400ff90 }, + { 0x13001feb, 0x2400ff90 }, + { 0x13001fec, 0x2400fff9 }, + { 0x13801fed, 0x60000002 }, + { 0x13001ff2, 0x14000000 }, + { 0x13001ff3, 0x14000009 }, + { 0x13001ff4, 0x14000000 }, + { 0x13801ff6, 0x14000001 }, + { 0x13001ff8, 0x2400ff80 }, + { 0x13001ff9, 0x2400ff80 }, + { 0x13001ffa, 0x2400ff82 }, + { 0x13001ffb, 0x2400ff82 }, + { 0x13001ffc, 0x2000fff7 }, + { 0x13801ffd, 0x60000001 }, + { 0x09802000, 0x7400000a }, + { 0x0980200b, 0x04000004 }, + { 0x09802010, 0x44000005 }, + { 0x09802016, 0x54000001 }, + { 0x09002018, 0x50000000 }, + { 0x09002019, 0x4c000000 }, + { 0x0900201a, 0x58000000 }, + { 0x0980201b, 0x50000001 }, + { 0x0900201d, 0x4c000000 }, + { 0x0900201e, 0x58000000 }, + { 0x0900201f, 0x50000000 }, + { 0x09802020, 0x54000007 }, + { 0x09002028, 0x6c000000 }, + { 0x09002029, 0x70000000 }, + { 0x0980202a, 0x04000004 }, + { 0x0900202f, 0x74000000 }, + { 0x09802030, 0x54000008 }, + { 0x09002039, 0x50000000 }, + { 0x0900203a, 0x4c000000 }, + { 0x0980203b, 0x54000003 }, + { 0x0980203f, 0x40000001 }, + { 0x09802041, 0x54000002 }, + { 0x09002044, 0x64000000 }, + { 0x09002045, 0x58000000 }, + { 0x09002046, 0x48000000 }, + { 0x09802047, 0x5400000a }, + { 0x09002052, 0x64000000 }, + { 0x09002053, 0x54000000 }, + { 0x09002054, 0x40000000 }, + { 0x09802055, 0x54000009 }, + { 0x0900205f, 0x74000000 }, + { 0x09802060, 0x04000003 }, + { 0x0980206a, 0x04000005 }, + { 0x09002070, 0x3c000000 }, + { 0x21002071, 0x14000000 }, + { 0x09802074, 0x3c000005 }, + { 0x0980207a, 0x64000002 }, + { 0x0900207d, 0x58000000 }, + { 0x0900207e, 0x48000000 }, + { 0x2100207f, 0x14000000 }, + { 0x09802080, 0x3c000009 }, + { 0x0980208a, 0x64000002 }, + { 0x0900208d, 0x58000000 }, + { 0x0900208e, 0x48000000 }, + { 0x21802090, 0x18000004 }, + { 0x098020a0, 0x5c000015 }, + { 0x1b8020d0, 0x3000000c }, + { 0x1b8020dd, 0x2c000003 }, + { 0x1b0020e1, 0x30000000 }, + { 0x1b8020e2, 0x2c000002 }, + { 0x1b8020e5, 0x30000006 }, + { 0x09802100, 0x68000001 }, + { 0x09002102, 0x24000000 }, + { 0x09802103, 0x68000003 }, + { 0x09002107, 0x24000000 }, + { 0x09802108, 0x68000001 }, + { 0x0900210a, 0x14000000 }, + { 0x0980210b, 0x24000002 }, + { 0x0980210e, 0x14000001 }, + { 0x09802110, 0x24000002 }, + { 0x09002113, 0x14000000 }, + { 0x09002114, 0x68000000 }, + { 0x09002115, 0x24000000 }, + { 0x09802116, 0x68000002 }, + { 0x09802119, 0x24000004 }, + { 0x0980211e, 0x68000005 }, + { 0x09002124, 0x24000000 }, + { 0x09002125, 0x68000000 }, + { 0x13002126, 0x2400e2a3 }, + { 0x09002127, 0x68000000 }, + { 0x09002128, 0x24000000 }, + { 0x09002129, 0x68000000 }, + { 0x2100212a, 0x2400df41 }, + { 0x2100212b, 0x2400dfba }, + { 0x0980212c, 0x24000001 }, + { 0x0900212e, 0x68000000 }, + { 0x0900212f, 0x14000000 }, + { 0x09802130, 0x24000001 }, + { 0x09002132, 0x68000000 }, + { 0x09002133, 0x24000000 }, + { 0x09002134, 0x14000000 }, + { 0x09802135, 0x1c000003 }, + { 0x09002139, 0x14000000 }, + { 0x0980213a, 0x68000001 }, + { 0x0980213c, 0x14000001 }, + { 0x0980213e, 0x24000001 }, + { 0x09802140, 0x64000004 }, + { 0x09002145, 0x24000000 }, + { 0x09802146, 0x14000003 }, + { 0x0900214a, 0x68000000 }, + { 0x0900214b, 0x64000000 }, + { 0x0900214c, 0x68000000 }, + { 0x09802153, 0x3c00000c }, + { 0x09002160, 0x38000010 }, + { 0x09002161, 0x38000010 }, + { 0x09002162, 0x38000010 }, + { 0x09002163, 0x38000010 }, + { 0x09002164, 0x38000010 }, + { 0x09002165, 0x38000010 }, + { 0x09002166, 0x38000010 }, + { 0x09002167, 0x38000010 }, + { 0x09002168, 0x38000010 }, + { 0x09002169, 0x38000010 }, + { 0x0900216a, 0x38000010 }, + { 0x0900216b, 0x38000010 }, + { 0x0900216c, 0x38000010 }, + { 0x0900216d, 0x38000010 }, + { 0x0900216e, 0x38000010 }, + { 0x0900216f, 0x38000010 }, + { 0x09002170, 0x3800fff0 }, + { 0x09002171, 0x3800fff0 }, + { 0x09002172, 0x3800fff0 }, + { 0x09002173, 0x3800fff0 }, + { 0x09002174, 0x3800fff0 }, + { 0x09002175, 0x3800fff0 }, + { 0x09002176, 0x3800fff0 }, + { 0x09002177, 0x3800fff0 }, + { 0x09002178, 0x3800fff0 }, + { 0x09002179, 0x3800fff0 }, + { 0x0900217a, 0x3800fff0 }, + { 0x0900217b, 0x3800fff0 }, + { 0x0900217c, 0x3800fff0 }, + { 0x0900217d, 0x3800fff0 }, + { 0x0900217e, 0x3800fff0 }, + { 0x0900217f, 0x3800fff0 }, + { 0x09802180, 0x38000003 }, + { 0x09802190, 0x64000004 }, + { 0x09802195, 0x68000004 }, + { 0x0980219a, 0x64000001 }, + { 0x0980219c, 0x68000003 }, + { 0x090021a0, 0x64000000 }, + { 0x098021a1, 0x68000001 }, + { 0x090021a3, 0x64000000 }, + { 0x098021a4, 0x68000001 }, + { 0x090021a6, 0x64000000 }, + { 0x098021a7, 0x68000006 }, + { 0x090021ae, 0x64000000 }, + { 0x098021af, 0x6800001e }, + { 0x098021ce, 0x64000001 }, + { 0x098021d0, 0x68000001 }, + { 0x090021d2, 0x64000000 }, + { 0x090021d3, 0x68000000 }, + { 0x090021d4, 0x64000000 }, + { 0x098021d5, 0x6800001e }, + { 0x098021f4, 0x6400010b }, + { 0x09802300, 0x68000007 }, + { 0x09802308, 0x64000003 }, + { 0x0980230c, 0x68000013 }, + { 0x09802320, 0x64000001 }, + { 0x09802322, 0x68000006 }, + { 0x09002329, 0x58000000 }, + { 0x0900232a, 0x48000000 }, + { 0x0980232b, 0x68000050 }, + { 0x0900237c, 0x64000000 }, + { 0x0980237d, 0x6800001d }, + { 0x0980239b, 0x64000018 }, + { 0x090023b4, 0x58000000 }, + { 0x090023b5, 0x48000000 }, + { 0x090023b6, 0x54000000 }, + { 0x098023b7, 0x68000024 }, + { 0x09802400, 0x68000026 }, + { 0x09802440, 0x6800000a }, + { 0x09802460, 0x3c00003b }, + { 0x0980249c, 0x68000019 }, + { 0x090024b6, 0x6800001a }, + { 0x090024b7, 0x6800001a }, + { 0x090024b8, 0x6800001a }, + { 0x090024b9, 0x6800001a }, + { 0x090024ba, 0x6800001a }, + { 0x090024bb, 0x6800001a }, + { 0x090024bc, 0x6800001a }, + { 0x090024bd, 0x6800001a }, + { 0x090024be, 0x6800001a }, + { 0x090024bf, 0x6800001a }, + { 0x090024c0, 0x6800001a }, + { 0x090024c1, 0x6800001a }, + { 0x090024c2, 0x6800001a }, + { 0x090024c3, 0x6800001a }, + { 0x090024c4, 0x6800001a }, + { 0x090024c5, 0x6800001a }, + { 0x090024c6, 0x6800001a }, + { 0x090024c7, 0x6800001a }, + { 0x090024c8, 0x6800001a }, + { 0x090024c9, 0x6800001a }, + { 0x090024ca, 0x6800001a }, + { 0x090024cb, 0x6800001a }, + { 0x090024cc, 0x6800001a }, + { 0x090024cd, 0x6800001a }, + { 0x090024ce, 0x6800001a }, + { 0x090024cf, 0x6800001a }, + { 0x090024d0, 0x6800ffe6 }, + { 0x090024d1, 0x6800ffe6 }, + { 0x090024d2, 0x6800ffe6 }, + { 0x090024d3, 0x6800ffe6 }, + { 0x090024d4, 0x6800ffe6 }, + { 0x090024d5, 0x6800ffe6 }, + { 0x090024d6, 0x6800ffe6 }, + { 0x090024d7, 0x6800ffe6 }, + { 0x090024d8, 0x6800ffe6 }, + { 0x090024d9, 0x6800ffe6 }, + { 0x090024da, 0x6800ffe6 }, + { 0x090024db, 0x6800ffe6 }, + { 0x090024dc, 0x6800ffe6 }, + { 0x090024dd, 0x6800ffe6 }, + { 0x090024de, 0x6800ffe6 }, + { 0x090024df, 0x6800ffe6 }, + { 0x090024e0, 0x6800ffe6 }, + { 0x090024e1, 0x6800ffe6 }, + { 0x090024e2, 0x6800ffe6 }, + { 0x090024e3, 0x6800ffe6 }, + { 0x090024e4, 0x6800ffe6 }, + { 0x090024e5, 0x6800ffe6 }, + { 0x090024e6, 0x6800ffe6 }, + { 0x090024e7, 0x6800ffe6 }, + { 0x090024e8, 0x6800ffe6 }, + { 0x090024e9, 0x6800ffe6 }, + { 0x098024ea, 0x3c000015 }, + { 0x09802500, 0x680000b6 }, + { 0x090025b7, 0x64000000 }, + { 0x098025b8, 0x68000008 }, + { 0x090025c1, 0x64000000 }, + { 0x098025c2, 0x68000035 }, + { 0x098025f8, 0x64000007 }, + { 0x09802600, 0x6800006e }, + { 0x0900266f, 0x64000000 }, + { 0x09802670, 0x6800002c }, + { 0x098026a0, 0x68000011 }, + { 0x09802701, 0x68000003 }, + { 0x09802706, 0x68000003 }, + { 0x0980270c, 0x6800001b }, + { 0x09802729, 0x68000022 }, + { 0x0900274d, 0x68000000 }, + { 0x0980274f, 0x68000003 }, + { 0x09002756, 0x68000000 }, + { 0x09802758, 0x68000006 }, + { 0x09802761, 0x68000006 }, + { 0x09002768, 0x58000000 }, + { 0x09002769, 0x48000000 }, + { 0x0900276a, 0x58000000 }, + { 0x0900276b, 0x48000000 }, + { 0x0900276c, 0x58000000 }, + { 0x0900276d, 0x48000000 }, + { 0x0900276e, 0x58000000 }, + { 0x0900276f, 0x48000000 }, + { 0x09002770, 0x58000000 }, + { 0x09002771, 0x48000000 }, + { 0x09002772, 0x58000000 }, + { 0x09002773, 0x48000000 }, + { 0x09002774, 0x58000000 }, + { 0x09002775, 0x48000000 }, + { 0x09802776, 0x3c00001d }, + { 0x09002794, 0x68000000 }, + { 0x09802798, 0x68000017 }, + { 0x098027b1, 0x6800000d }, + { 0x098027c0, 0x64000004 }, + { 0x090027c5, 0x58000000 }, + { 0x090027c6, 0x48000000 }, + { 0x098027d0, 0x64000015 }, + { 0x090027e6, 0x58000000 }, + { 0x090027e7, 0x48000000 }, + { 0x090027e8, 0x58000000 }, + { 0x090027e9, 0x48000000 }, + { 0x090027ea, 0x58000000 }, + { 0x090027eb, 0x48000000 }, + { 0x098027f0, 0x6400000f }, + { 0x04802800, 0x680000ff }, + { 0x09802900, 0x64000082 }, + { 0x09002983, 0x58000000 }, + { 0x09002984, 0x48000000 }, + { 0x09002985, 0x58000000 }, + { 0x09002986, 0x48000000 }, + { 0x09002987, 0x58000000 }, + { 0x09002988, 0x48000000 }, + { 0x09002989, 0x58000000 }, + { 0x0900298a, 0x48000000 }, + { 0x0900298b, 0x58000000 }, + { 0x0900298c, 0x48000000 }, + { 0x0900298d, 0x58000000 }, + { 0x0900298e, 0x48000000 }, + { 0x0900298f, 0x58000000 }, + { 0x09002990, 0x48000000 }, + { 0x09002991, 0x58000000 }, + { 0x09002992, 0x48000000 }, + { 0x09002993, 0x58000000 }, + { 0x09002994, 0x48000000 }, + { 0x09002995, 0x58000000 }, + { 0x09002996, 0x48000000 }, + { 0x09002997, 0x58000000 }, + { 0x09002998, 0x48000000 }, + { 0x09802999, 0x6400003e }, + { 0x090029d8, 0x58000000 }, + { 0x090029d9, 0x48000000 }, + { 0x090029da, 0x58000000 }, + { 0x090029db, 0x48000000 }, + { 0x098029dc, 0x6400001f }, + { 0x090029fc, 0x58000000 }, + { 0x090029fd, 0x48000000 }, + { 0x098029fe, 0x64000101 }, + { 0x09802b00, 0x68000013 }, + { 0x11002c00, 0x24000030 }, + { 0x11002c01, 0x24000030 }, + { 0x11002c02, 0x24000030 }, + { 0x11002c03, 0x24000030 }, + { 0x11002c04, 0x24000030 }, + { 0x11002c05, 0x24000030 }, + { 0x11002c06, 0x24000030 }, + { 0x11002c07, 0x24000030 }, + { 0x11002c08, 0x24000030 }, + { 0x11002c09, 0x24000030 }, + { 0x11002c0a, 0x24000030 }, + { 0x11002c0b, 0x24000030 }, + { 0x11002c0c, 0x24000030 }, + { 0x11002c0d, 0x24000030 }, + { 0x11002c0e, 0x24000030 }, + { 0x11002c0f, 0x24000030 }, + { 0x11002c10, 0x24000030 }, + { 0x11002c11, 0x24000030 }, + { 0x11002c12, 0x24000030 }, + { 0x11002c13, 0x24000030 }, + { 0x11002c14, 0x24000030 }, + { 0x11002c15, 0x24000030 }, + { 0x11002c16, 0x24000030 }, + { 0x11002c17, 0x24000030 }, + { 0x11002c18, 0x24000030 }, + { 0x11002c19, 0x24000030 }, + { 0x11002c1a, 0x24000030 }, + { 0x11002c1b, 0x24000030 }, + { 0x11002c1c, 0x24000030 }, + { 0x11002c1d, 0x24000030 }, + { 0x11002c1e, 0x24000030 }, + { 0x11002c1f, 0x24000030 }, + { 0x11002c20, 0x24000030 }, + { 0x11002c21, 0x24000030 }, + { 0x11002c22, 0x24000030 }, + { 0x11002c23, 0x24000030 }, + { 0x11002c24, 0x24000030 }, + { 0x11002c25, 0x24000030 }, + { 0x11002c26, 0x24000030 }, + { 0x11002c27, 0x24000030 }, + { 0x11002c28, 0x24000030 }, + { 0x11002c29, 0x24000030 }, + { 0x11002c2a, 0x24000030 }, + { 0x11002c2b, 0x24000030 }, + { 0x11002c2c, 0x24000030 }, + { 0x11002c2d, 0x24000030 }, + { 0x11002c2e, 0x24000030 }, + { 0x11002c30, 0x1400ffd0 }, + { 0x11002c31, 0x1400ffd0 }, + { 0x11002c32, 0x1400ffd0 }, + { 0x11002c33, 0x1400ffd0 }, + { 0x11002c34, 0x1400ffd0 }, + { 0x11002c35, 0x1400ffd0 }, + { 0x11002c36, 0x1400ffd0 }, + { 0x11002c37, 0x1400ffd0 }, + { 0x11002c38, 0x1400ffd0 }, + { 0x11002c39, 0x1400ffd0 }, + { 0x11002c3a, 0x1400ffd0 }, + { 0x11002c3b, 0x1400ffd0 }, + { 0x11002c3c, 0x1400ffd0 }, + { 0x11002c3d, 0x1400ffd0 }, + { 0x11002c3e, 0x1400ffd0 }, + { 0x11002c3f, 0x1400ffd0 }, + { 0x11002c40, 0x1400ffd0 }, + { 0x11002c41, 0x1400ffd0 }, + { 0x11002c42, 0x1400ffd0 }, + { 0x11002c43, 0x1400ffd0 }, + { 0x11002c44, 0x1400ffd0 }, + { 0x11002c45, 0x1400ffd0 }, + { 0x11002c46, 0x1400ffd0 }, + { 0x11002c47, 0x1400ffd0 }, + { 0x11002c48, 0x1400ffd0 }, + { 0x11002c49, 0x1400ffd0 }, + { 0x11002c4a, 0x1400ffd0 }, + { 0x11002c4b, 0x1400ffd0 }, + { 0x11002c4c, 0x1400ffd0 }, + { 0x11002c4d, 0x1400ffd0 }, + { 0x11002c4e, 0x1400ffd0 }, + { 0x11002c4f, 0x1400ffd0 }, + { 0x11002c50, 0x1400ffd0 }, + { 0x11002c51, 0x1400ffd0 }, + { 0x11002c52, 0x1400ffd0 }, + { 0x11002c53, 0x1400ffd0 }, + { 0x11002c54, 0x1400ffd0 }, + { 0x11002c55, 0x1400ffd0 }, + { 0x11002c56, 0x1400ffd0 }, + { 0x11002c57, 0x1400ffd0 }, + { 0x11002c58, 0x1400ffd0 }, + { 0x11002c59, 0x1400ffd0 }, + { 0x11002c5a, 0x1400ffd0 }, + { 0x11002c5b, 0x1400ffd0 }, + { 0x11002c5c, 0x1400ffd0 }, + { 0x11002c5d, 0x1400ffd0 }, + { 0x11002c5e, 0x1400ffd0 }, + { 0x0a002c80, 0x24000001 }, + { 0x0a002c81, 0x1400ffff }, + { 0x0a002c82, 0x24000001 }, + { 0x0a002c83, 0x1400ffff }, + { 0x0a002c84, 0x24000001 }, + { 0x0a002c85, 0x1400ffff }, + { 0x0a002c86, 0x24000001 }, + { 0x0a002c87, 0x1400ffff }, + { 0x0a002c88, 0x24000001 }, + { 0x0a002c89, 0x1400ffff }, + { 0x0a002c8a, 0x24000001 }, + { 0x0a002c8b, 0x1400ffff }, + { 0x0a002c8c, 0x24000001 }, + { 0x0a002c8d, 0x1400ffff }, + { 0x0a002c8e, 0x24000001 }, + { 0x0a002c8f, 0x1400ffff }, + { 0x0a002c90, 0x24000001 }, + { 0x0a002c91, 0x1400ffff }, + { 0x0a002c92, 0x24000001 }, + { 0x0a002c93, 0x1400ffff }, + { 0x0a002c94, 0x24000001 }, + { 0x0a002c95, 0x1400ffff }, + { 0x0a002c96, 0x24000001 }, + { 0x0a002c97, 0x1400ffff }, + { 0x0a002c98, 0x24000001 }, + { 0x0a002c99, 0x1400ffff }, + { 0x0a002c9a, 0x24000001 }, + { 0x0a002c9b, 0x1400ffff }, + { 0x0a002c9c, 0x24000001 }, + { 0x0a002c9d, 0x1400ffff }, + { 0x0a002c9e, 0x24000001 }, + { 0x0a002c9f, 0x1400ffff }, + { 0x0a002ca0, 0x24000001 }, + { 0x0a002ca1, 0x1400ffff }, + { 0x0a002ca2, 0x24000001 }, + { 0x0a002ca3, 0x1400ffff }, + { 0x0a002ca4, 0x24000001 }, + { 0x0a002ca5, 0x1400ffff }, + { 0x0a002ca6, 0x24000001 }, + { 0x0a002ca7, 0x1400ffff }, + { 0x0a002ca8, 0x24000001 }, + { 0x0a002ca9, 0x1400ffff }, + { 0x0a002caa, 0x24000001 }, + { 0x0a002cab, 0x1400ffff }, + { 0x0a002cac, 0x24000001 }, + { 0x0a002cad, 0x1400ffff }, + { 0x0a002cae, 0x24000001 }, + { 0x0a002caf, 0x1400ffff }, + { 0x0a002cb0, 0x24000001 }, + { 0x0a002cb1, 0x1400ffff }, + { 0x0a002cb2, 0x24000001 }, + { 0x0a002cb3, 0x1400ffff }, + { 0x0a002cb4, 0x24000001 }, + { 0x0a002cb5, 0x1400ffff }, + { 0x0a002cb6, 0x24000001 }, + { 0x0a002cb7, 0x1400ffff }, + { 0x0a002cb8, 0x24000001 }, + { 0x0a002cb9, 0x1400ffff }, + { 0x0a002cba, 0x24000001 }, + { 0x0a002cbb, 0x1400ffff }, + { 0x0a002cbc, 0x24000001 }, + { 0x0a002cbd, 0x1400ffff }, + { 0x0a002cbe, 0x24000001 }, + { 0x0a002cbf, 0x1400ffff }, + { 0x0a002cc0, 0x24000001 }, + { 0x0a002cc1, 0x1400ffff }, + { 0x0a002cc2, 0x24000001 }, + { 0x0a002cc3, 0x1400ffff }, + { 0x0a002cc4, 0x24000001 }, + { 0x0a002cc5, 0x1400ffff }, + { 0x0a002cc6, 0x24000001 }, + { 0x0a002cc7, 0x1400ffff }, + { 0x0a002cc8, 0x24000001 }, + { 0x0a002cc9, 0x1400ffff }, + { 0x0a002cca, 0x24000001 }, + { 0x0a002ccb, 0x1400ffff }, + { 0x0a002ccc, 0x24000001 }, + { 0x0a002ccd, 0x1400ffff }, + { 0x0a002cce, 0x24000001 }, + { 0x0a002ccf, 0x1400ffff }, + { 0x0a002cd0, 0x24000001 }, + { 0x0a002cd1, 0x1400ffff }, + { 0x0a002cd2, 0x24000001 }, + { 0x0a002cd3, 0x1400ffff }, + { 0x0a002cd4, 0x24000001 }, + { 0x0a002cd5, 0x1400ffff }, + { 0x0a002cd6, 0x24000001 }, + { 0x0a002cd7, 0x1400ffff }, + { 0x0a002cd8, 0x24000001 }, + { 0x0a002cd9, 0x1400ffff }, + { 0x0a002cda, 0x24000001 }, + { 0x0a002cdb, 0x1400ffff }, + { 0x0a002cdc, 0x24000001 }, + { 0x0a002cdd, 0x1400ffff }, + { 0x0a002cde, 0x24000001 }, + { 0x0a002cdf, 0x1400ffff }, + { 0x0a002ce0, 0x24000001 }, + { 0x0a002ce1, 0x1400ffff }, + { 0x0a002ce2, 0x24000001 }, + { 0x0a002ce3, 0x1400ffff }, + { 0x0a002ce4, 0x14000000 }, + { 0x0a802ce5, 0x68000005 }, + { 0x0a802cf9, 0x54000003 }, + { 0x0a002cfd, 0x3c000000 }, + { 0x0a802cfe, 0x54000001 }, + { 0x10002d00, 0x1400e3a0 }, + { 0x10002d01, 0x1400e3a0 }, + { 0x10002d02, 0x1400e3a0 }, + { 0x10002d03, 0x1400e3a0 }, + { 0x10002d04, 0x1400e3a0 }, + { 0x10002d05, 0x1400e3a0 }, + { 0x10002d06, 0x1400e3a0 }, + { 0x10002d07, 0x1400e3a0 }, + { 0x10002d08, 0x1400e3a0 }, + { 0x10002d09, 0x1400e3a0 }, + { 0x10002d0a, 0x1400e3a0 }, + { 0x10002d0b, 0x1400e3a0 }, + { 0x10002d0c, 0x1400e3a0 }, + { 0x10002d0d, 0x1400e3a0 }, + { 0x10002d0e, 0x1400e3a0 }, + { 0x10002d0f, 0x1400e3a0 }, + { 0x10002d10, 0x1400e3a0 }, + { 0x10002d11, 0x1400e3a0 }, + { 0x10002d12, 0x1400e3a0 }, + { 0x10002d13, 0x1400e3a0 }, + { 0x10002d14, 0x1400e3a0 }, + { 0x10002d15, 0x1400e3a0 }, + { 0x10002d16, 0x1400e3a0 }, + { 0x10002d17, 0x1400e3a0 }, + { 0x10002d18, 0x1400e3a0 }, + { 0x10002d19, 0x1400e3a0 }, + { 0x10002d1a, 0x1400e3a0 }, + { 0x10002d1b, 0x1400e3a0 }, + { 0x10002d1c, 0x1400e3a0 }, + { 0x10002d1d, 0x1400e3a0 }, + { 0x10002d1e, 0x1400e3a0 }, + { 0x10002d1f, 0x1400e3a0 }, + { 0x10002d20, 0x1400e3a0 }, + { 0x10002d21, 0x1400e3a0 }, + { 0x10002d22, 0x1400e3a0 }, + { 0x10002d23, 0x1400e3a0 }, + { 0x10002d24, 0x1400e3a0 }, + { 0x10002d25, 0x1400e3a0 }, + { 0x3a802d30, 0x1c000035 }, + { 0x3a002d6f, 0x18000000 }, + { 0x0f802d80, 0x1c000016 }, + { 0x0f802da0, 0x1c000006 }, + { 0x0f802da8, 0x1c000006 }, + { 0x0f802db0, 0x1c000006 }, + { 0x0f802db8, 0x1c000006 }, + { 0x0f802dc0, 0x1c000006 }, + { 0x0f802dc8, 0x1c000006 }, + { 0x0f802dd0, 0x1c000006 }, + { 0x0f802dd8, 0x1c000006 }, + { 0x09802e00, 0x54000001 }, + { 0x09002e02, 0x50000000 }, + { 0x09002e03, 0x4c000000 }, + { 0x09002e04, 0x50000000 }, + { 0x09002e05, 0x4c000000 }, + { 0x09802e06, 0x54000002 }, + { 0x09002e09, 0x50000000 }, + { 0x09002e0a, 0x4c000000 }, + { 0x09002e0b, 0x54000000 }, + { 0x09002e0c, 0x50000000 }, + { 0x09002e0d, 0x4c000000 }, + { 0x09802e0e, 0x54000008 }, + { 0x09002e17, 0x44000000 }, + { 0x09002e1c, 0x50000000 }, + { 0x09002e1d, 0x4c000000 }, + { 0x16802e80, 0x68000019 }, + { 0x16802e9b, 0x68000058 }, + { 0x16802f00, 0x680000d5 }, + { 0x09802ff0, 0x6800000b }, + { 0x09003000, 0x74000000 }, + { 0x09803001, 0x54000002 }, + { 0x09003004, 0x68000000 }, + { 0x16003005, 0x18000000 }, + { 0x09003006, 0x1c000000 }, + { 0x16003007, 0x38000000 }, + { 0x09003008, 0x58000000 }, + { 0x09003009, 0x48000000 }, + { 0x0900300a, 0x58000000 }, + { 0x0900300b, 0x48000000 }, + { 0x0900300c, 0x58000000 }, + { 0x0900300d, 0x48000000 }, + { 0x0900300e, 0x58000000 }, + { 0x0900300f, 0x48000000 }, + { 0x09003010, 0x58000000 }, + { 0x09003011, 0x48000000 }, + { 0x09803012, 0x68000001 }, + { 0x09003014, 0x58000000 }, + { 0x09003015, 0x48000000 }, + { 0x09003016, 0x58000000 }, + { 0x09003017, 0x48000000 }, + { 0x09003018, 0x58000000 }, + { 0x09003019, 0x48000000 }, + { 0x0900301a, 0x58000000 }, + { 0x0900301b, 0x48000000 }, + { 0x0900301c, 0x44000000 }, + { 0x0900301d, 0x58000000 }, + { 0x0980301e, 0x48000001 }, + { 0x09003020, 0x68000000 }, + { 0x16803021, 0x38000008 }, + { 0x1b80302a, 0x30000005 }, + { 0x09003030, 0x44000000 }, + { 0x09803031, 0x18000004 }, + { 0x09803036, 0x68000001 }, + { 0x16803038, 0x38000002 }, + { 0x1600303b, 0x18000000 }, + { 0x0900303c, 0x1c000000 }, + { 0x0900303d, 0x54000000 }, + { 0x0980303e, 0x68000001 }, + { 0x1a803041, 0x1c000055 }, + { 0x1b803099, 0x30000001 }, + { 0x0980309b, 0x60000001 }, + { 0x1a80309d, 0x18000001 }, + { 0x1a00309f, 0x1c000000 }, + { 0x090030a0, 0x44000000 }, + { 0x1d8030a1, 0x1c000059 }, + { 0x090030fb, 0x54000000 }, + { 0x098030fc, 0x18000002 }, + { 0x1d0030ff, 0x1c000000 }, + { 0x03803105, 0x1c000027 }, + { 0x17803131, 0x1c00005d }, + { 0x09803190, 0x68000001 }, + { 0x09803192, 0x3c000003 }, + { 0x09803196, 0x68000009 }, + { 0x038031a0, 0x1c000017 }, + { 0x098031c0, 0x6800000f }, + { 0x1d8031f0, 0x1c00000f }, + { 0x17803200, 0x6800001e }, + { 0x09803220, 0x3c000009 }, + { 0x0980322a, 0x68000019 }, + { 0x09003250, 0x68000000 }, + { 0x09803251, 0x3c00000e }, + { 0x17803260, 0x6800001f }, + { 0x09803280, 0x3c000009 }, + { 0x0980328a, 0x68000026 }, + { 0x098032b1, 0x3c00000e }, + { 0x098032c0, 0x6800003e }, + { 0x09803300, 0x680000ff }, + { 0x16803400, 0x1c0019b5 }, + { 0x09804dc0, 0x6800003f }, + { 0x16804e00, 0x1c0051bb }, + { 0x3c80a000, 0x1c000014 }, + { 0x3c00a015, 0x18000000 }, + { 0x3c80a016, 0x1c000476 }, + { 0x3c80a490, 0x68000036 }, + { 0x0980a700, 0x60000016 }, + { 0x3080a800, 0x1c000001 }, + { 0x3000a802, 0x28000000 }, + { 0x3080a803, 0x1c000002 }, + { 0x3000a806, 0x30000000 }, + { 0x3080a807, 0x1c000003 }, + { 0x3000a80b, 0x30000000 }, + { 0x3080a80c, 0x1c000016 }, + { 0x3080a823, 0x28000001 }, + { 0x3080a825, 0x30000001 }, + { 0x3000a827, 0x28000000 }, + { 0x3080a828, 0x68000003 }, + { 0x1780ac00, 0x1c002ba3 }, + { 0x0980d800, 0x1000037f }, + { 0x0980db80, 0x1000007f }, + { 0x0980dc00, 0x100003ff }, + { 0x0980e000, 0x0c0018ff }, + { 0x1680f900, 0x1c00012d }, + { 0x1680fa30, 0x1c00003a }, + { 0x1680fa70, 0x1c000069 }, + { 0x2180fb00, 0x14000006 }, + { 0x0180fb13, 0x14000004 }, + { 0x1900fb1d, 0x1c000000 }, + { 0x1900fb1e, 0x30000000 }, + { 0x1980fb1f, 0x1c000009 }, + { 0x1900fb29, 0x64000000 }, + { 0x1980fb2a, 0x1c00000c }, + { 0x1980fb38, 0x1c000004 }, + { 0x1900fb3e, 0x1c000000 }, + { 0x1980fb40, 0x1c000001 }, + { 0x1980fb43, 0x1c000001 }, + { 0x1980fb46, 0x1c00006b }, + { 0x0080fbd3, 0x1c00016a }, + { 0x0900fd3e, 0x58000000 }, + { 0x0900fd3f, 0x48000000 }, + { 0x0080fd50, 0x1c00003f }, + { 0x0080fd92, 0x1c000035 }, + { 0x0080fdf0, 0x1c00000b }, + { 0x0000fdfc, 0x5c000000 }, + { 0x0900fdfd, 0x68000000 }, + { 0x1b80fe00, 0x3000000f }, + { 0x0980fe10, 0x54000006 }, + { 0x0900fe17, 0x58000000 }, + { 0x0900fe18, 0x48000000 }, + { 0x0900fe19, 0x54000000 }, + { 0x1b80fe20, 0x30000003 }, + { 0x0900fe30, 0x54000000 }, + { 0x0980fe31, 0x44000001 }, + { 0x0980fe33, 0x40000001 }, + { 0x0900fe35, 0x58000000 }, + { 0x0900fe36, 0x48000000 }, + { 0x0900fe37, 0x58000000 }, + { 0x0900fe38, 0x48000000 }, + { 0x0900fe39, 0x58000000 }, + { 0x0900fe3a, 0x48000000 }, + { 0x0900fe3b, 0x58000000 }, + { 0x0900fe3c, 0x48000000 }, + { 0x0900fe3d, 0x58000000 }, + { 0x0900fe3e, 0x48000000 }, + { 0x0900fe3f, 0x58000000 }, + { 0x0900fe40, 0x48000000 }, + { 0x0900fe41, 0x58000000 }, + { 0x0900fe42, 0x48000000 }, + { 0x0900fe43, 0x58000000 }, + { 0x0900fe44, 0x48000000 }, + { 0x0980fe45, 0x54000001 }, + { 0x0900fe47, 0x58000000 }, + { 0x0900fe48, 0x48000000 }, + { 0x0980fe49, 0x54000003 }, + { 0x0980fe4d, 0x40000002 }, + { 0x0980fe50, 0x54000002 }, + { 0x0980fe54, 0x54000003 }, + { 0x0900fe58, 0x44000000 }, + { 0x0900fe59, 0x58000000 }, + { 0x0900fe5a, 0x48000000 }, + { 0x0900fe5b, 0x58000000 }, + { 0x0900fe5c, 0x48000000 }, + { 0x0900fe5d, 0x58000000 }, + { 0x0900fe5e, 0x48000000 }, + { 0x0980fe5f, 0x54000002 }, + { 0x0900fe62, 0x64000000 }, + { 0x0900fe63, 0x44000000 }, + { 0x0980fe64, 0x64000002 }, + { 0x0900fe68, 0x54000000 }, + { 0x0900fe69, 0x5c000000 }, + { 0x0980fe6a, 0x54000001 }, + { 0x0080fe70, 0x1c000004 }, + { 0x0080fe76, 0x1c000086 }, + { 0x0900feff, 0x04000000 }, + { 0x0980ff01, 0x54000002 }, + { 0x0900ff04, 0x5c000000 }, + { 0x0980ff05, 0x54000002 }, + { 0x0900ff08, 0x58000000 }, + { 0x0900ff09, 0x48000000 }, + { 0x0900ff0a, 0x54000000 }, + { 0x0900ff0b, 0x64000000 }, + { 0x0900ff0c, 0x54000000 }, + { 0x0900ff0d, 0x44000000 }, + { 0x0980ff0e, 0x54000001 }, + { 0x0980ff10, 0x34000009 }, + { 0x0980ff1a, 0x54000001 }, + { 0x0980ff1c, 0x64000002 }, + { 0x0980ff1f, 0x54000001 }, + { 0x2100ff21, 0x24000020 }, + { 0x2100ff22, 0x24000020 }, + { 0x2100ff23, 0x24000020 }, + { 0x2100ff24, 0x24000020 }, + { 0x2100ff25, 0x24000020 }, + { 0x2100ff26, 0x24000020 }, + { 0x2100ff27, 0x24000020 }, + { 0x2100ff28, 0x24000020 }, + { 0x2100ff29, 0x24000020 }, + { 0x2100ff2a, 0x24000020 }, + { 0x2100ff2b, 0x24000020 }, + { 0x2100ff2c, 0x24000020 }, + { 0x2100ff2d, 0x24000020 }, + { 0x2100ff2e, 0x24000020 }, + { 0x2100ff2f, 0x24000020 }, + { 0x2100ff30, 0x24000020 }, + { 0x2100ff31, 0x24000020 }, + { 0x2100ff32, 0x24000020 }, + { 0x2100ff33, 0x24000020 }, + { 0x2100ff34, 0x24000020 }, + { 0x2100ff35, 0x24000020 }, + { 0x2100ff36, 0x24000020 }, + { 0x2100ff37, 0x24000020 }, + { 0x2100ff38, 0x24000020 }, + { 0x2100ff39, 0x24000020 }, + { 0x2100ff3a, 0x24000020 }, + { 0x0900ff3b, 0x58000000 }, + { 0x0900ff3c, 0x54000000 }, + { 0x0900ff3d, 0x48000000 }, + { 0x0900ff3e, 0x60000000 }, + { 0x0900ff3f, 0x40000000 }, + { 0x0900ff40, 0x60000000 }, + { 0x2100ff41, 0x1400ffe0 }, + { 0x2100ff42, 0x1400ffe0 }, + { 0x2100ff43, 0x1400ffe0 }, + { 0x2100ff44, 0x1400ffe0 }, + { 0x2100ff45, 0x1400ffe0 }, + { 0x2100ff46, 0x1400ffe0 }, + { 0x2100ff47, 0x1400ffe0 }, + { 0x2100ff48, 0x1400ffe0 }, + { 0x2100ff49, 0x1400ffe0 }, + { 0x2100ff4a, 0x1400ffe0 }, + { 0x2100ff4b, 0x1400ffe0 }, + { 0x2100ff4c, 0x1400ffe0 }, + { 0x2100ff4d, 0x1400ffe0 }, + { 0x2100ff4e, 0x1400ffe0 }, + { 0x2100ff4f, 0x1400ffe0 }, + { 0x2100ff50, 0x1400ffe0 }, + { 0x2100ff51, 0x1400ffe0 }, + { 0x2100ff52, 0x1400ffe0 }, + { 0x2100ff53, 0x1400ffe0 }, + { 0x2100ff54, 0x1400ffe0 }, + { 0x2100ff55, 0x1400ffe0 }, + { 0x2100ff56, 0x1400ffe0 }, + { 0x2100ff57, 0x1400ffe0 }, + { 0x2100ff58, 0x1400ffe0 }, + { 0x2100ff59, 0x1400ffe0 }, + { 0x2100ff5a, 0x1400ffe0 }, + { 0x0900ff5b, 0x58000000 }, + { 0x0900ff5c, 0x64000000 }, + { 0x0900ff5d, 0x48000000 }, + { 0x0900ff5e, 0x64000000 }, + { 0x0900ff5f, 0x58000000 }, + { 0x0900ff60, 0x48000000 }, + { 0x0900ff61, 0x54000000 }, + { 0x0900ff62, 0x58000000 }, + { 0x0900ff63, 0x48000000 }, + { 0x0980ff64, 0x54000001 }, + { 0x1d80ff66, 0x1c000009 }, + { 0x0900ff70, 0x18000000 }, + { 0x1d80ff71, 0x1c00002c }, + { 0x0980ff9e, 0x18000001 }, + { 0x1780ffa0, 0x1c00001e }, + { 0x1780ffc2, 0x1c000005 }, + { 0x1780ffca, 0x1c000005 }, + { 0x1780ffd2, 0x1c000005 }, + { 0x1780ffda, 0x1c000002 }, + { 0x0980ffe0, 0x5c000001 }, + { 0x0900ffe2, 0x64000000 }, + { 0x0900ffe3, 0x60000000 }, + { 0x0900ffe4, 0x68000000 }, + { 0x0980ffe5, 0x5c000001 }, + { 0x0900ffe8, 0x68000000 }, + { 0x0980ffe9, 0x64000003 }, + { 0x0980ffed, 0x68000001 }, + { 0x0980fff9, 0x04000002 }, + { 0x0980fffc, 0x68000001 }, + { 0x23810000, 0x1c00000b }, + { 0x2381000d, 0x1c000019 }, + { 0x23810028, 0x1c000012 }, + { 0x2381003c, 0x1c000001 }, + { 0x2381003f, 0x1c00000e }, + { 0x23810050, 0x1c00000d }, + { 0x23810080, 0x1c00007a }, + { 0x09810100, 0x54000001 }, + { 0x09010102, 0x68000000 }, + { 0x09810107, 0x3c00002c }, + { 0x09810137, 0x68000008 }, + { 0x13810140, 0x38000034 }, + { 0x13810175, 0x3c000003 }, + { 0x13810179, 0x68000010 }, + { 0x1301018a, 0x3c000000 }, + { 0x29810300, 0x1c00001e }, + { 0x29810320, 0x3c000003 }, + { 0x12810330, 0x1c000019 }, + { 0x1201034a, 0x38000000 }, + { 0x3b810380, 0x1c00001d }, + { 0x3b01039f, 0x54000000 }, + { 0x2a8103a0, 0x1c000023 }, + { 0x2a8103c8, 0x1c000007 }, + { 0x2a0103d0, 0x68000000 }, + { 0x2a8103d1, 0x38000004 }, + { 0x0d010400, 0x24000028 }, + { 0x0d010401, 0x24000028 }, + { 0x0d010402, 0x24000028 }, + { 0x0d010403, 0x24000028 }, + { 0x0d010404, 0x24000028 }, + { 0x0d010405, 0x24000028 }, + { 0x0d010406, 0x24000028 }, + { 0x0d010407, 0x24000028 }, + { 0x0d010408, 0x24000028 }, + { 0x0d010409, 0x24000028 }, + { 0x0d01040a, 0x24000028 }, + { 0x0d01040b, 0x24000028 }, + { 0x0d01040c, 0x24000028 }, + { 0x0d01040d, 0x24000028 }, + { 0x0d01040e, 0x24000028 }, + { 0x0d01040f, 0x24000028 }, + { 0x0d010410, 0x24000028 }, + { 0x0d010411, 0x24000028 }, + { 0x0d010412, 0x24000028 }, + { 0x0d010413, 0x24000028 }, + { 0x0d010414, 0x24000028 }, + { 0x0d010415, 0x24000028 }, + { 0x0d010416, 0x24000028 }, + { 0x0d010417, 0x24000028 }, + { 0x0d010418, 0x24000028 }, + { 0x0d010419, 0x24000028 }, + { 0x0d01041a, 0x24000028 }, + { 0x0d01041b, 0x24000028 }, + { 0x0d01041c, 0x24000028 }, + { 0x0d01041d, 0x24000028 }, + { 0x0d01041e, 0x24000028 }, + { 0x0d01041f, 0x24000028 }, + { 0x0d010420, 0x24000028 }, + { 0x0d010421, 0x24000028 }, + { 0x0d010422, 0x24000028 }, + { 0x0d010423, 0x24000028 }, + { 0x0d010424, 0x24000028 }, + { 0x0d010425, 0x24000028 }, + { 0x0d010426, 0x24000028 }, + { 0x0d010427, 0x24000028 }, + { 0x0d010428, 0x1400ffd8 }, + { 0x0d010429, 0x1400ffd8 }, + { 0x0d01042a, 0x1400ffd8 }, + { 0x0d01042b, 0x1400ffd8 }, + { 0x0d01042c, 0x1400ffd8 }, + { 0x0d01042d, 0x1400ffd8 }, + { 0x0d01042e, 0x1400ffd8 }, + { 0x0d01042f, 0x1400ffd8 }, + { 0x0d010430, 0x1400ffd8 }, + { 0x0d010431, 0x1400ffd8 }, + { 0x0d010432, 0x1400ffd8 }, + { 0x0d010433, 0x1400ffd8 }, + { 0x0d010434, 0x1400ffd8 }, + { 0x0d010435, 0x1400ffd8 }, + { 0x0d010436, 0x1400ffd8 }, + { 0x0d010437, 0x1400ffd8 }, + { 0x0d010438, 0x1400ffd8 }, + { 0x0d010439, 0x1400ffd8 }, + { 0x0d01043a, 0x1400ffd8 }, + { 0x0d01043b, 0x1400ffd8 }, + { 0x0d01043c, 0x1400ffd8 }, + { 0x0d01043d, 0x1400ffd8 }, + { 0x0d01043e, 0x1400ffd8 }, + { 0x0d01043f, 0x1400ffd8 }, + { 0x0d010440, 0x1400ffd8 }, + { 0x0d010441, 0x1400ffd8 }, + { 0x0d010442, 0x1400ffd8 }, + { 0x0d010443, 0x1400ffd8 }, + { 0x0d010444, 0x1400ffd8 }, + { 0x0d010445, 0x1400ffd8 }, + { 0x0d010446, 0x1400ffd8 }, + { 0x0d010447, 0x1400ffd8 }, + { 0x0d010448, 0x1400ffd8 }, + { 0x0d010449, 0x1400ffd8 }, + { 0x0d01044a, 0x1400ffd8 }, + { 0x0d01044b, 0x1400ffd8 }, + { 0x0d01044c, 0x1400ffd8 }, + { 0x0d01044d, 0x1400ffd8 }, + { 0x0d01044e, 0x1400ffd8 }, + { 0x0d01044f, 0x1400ffd8 }, + { 0x2e810450, 0x1c00004d }, + { 0x2c8104a0, 0x34000009 }, + { 0x0b810800, 0x1c000005 }, + { 0x0b010808, 0x1c000000 }, + { 0x0b81080a, 0x1c00002b }, + { 0x0b810837, 0x1c000001 }, + { 0x0b01083c, 0x1c000000 }, + { 0x0b01083f, 0x1c000000 }, + { 0x1e010a00, 0x1c000000 }, + { 0x1e810a01, 0x30000002 }, + { 0x1e810a05, 0x30000001 }, + { 0x1e810a0c, 0x30000003 }, + { 0x1e810a10, 0x1c000003 }, + { 0x1e810a15, 0x1c000002 }, + { 0x1e810a19, 0x1c00001a }, + { 0x1e810a38, 0x30000002 }, + { 0x1e010a3f, 0x30000000 }, + { 0x1e810a40, 0x3c000007 }, + { 0x1e810a50, 0x54000008 }, + { 0x0981d000, 0x680000f5 }, + { 0x0981d100, 0x68000026 }, + { 0x0981d12a, 0x6800003a }, + { 0x0981d165, 0x28000001 }, + { 0x1b81d167, 0x30000002 }, + { 0x0981d16a, 0x68000002 }, + { 0x0981d16d, 0x28000005 }, + { 0x0981d173, 0x04000007 }, + { 0x1b81d17b, 0x30000007 }, + { 0x0981d183, 0x68000001 }, + { 0x1b81d185, 0x30000006 }, + { 0x0981d18c, 0x6800001d }, + { 0x1b81d1aa, 0x30000003 }, + { 0x0981d1ae, 0x6800002f }, + { 0x1381d200, 0x68000041 }, + { 0x1381d242, 0x30000002 }, + { 0x1301d245, 0x68000000 }, + { 0x0981d300, 0x68000056 }, + { 0x0981d400, 0x24000019 }, + { 0x0981d41a, 0x14000019 }, + { 0x0981d434, 0x24000019 }, + { 0x0981d44e, 0x14000006 }, + { 0x0981d456, 0x14000011 }, + { 0x0981d468, 0x24000019 }, + { 0x0981d482, 0x14000019 }, + { 0x0901d49c, 0x24000000 }, + { 0x0981d49e, 0x24000001 }, + { 0x0901d4a2, 0x24000000 }, + { 0x0981d4a5, 0x24000001 }, + { 0x0981d4a9, 0x24000003 }, + { 0x0981d4ae, 0x24000007 }, + { 0x0981d4b6, 0x14000003 }, + { 0x0901d4bb, 0x14000000 }, + { 0x0981d4bd, 0x14000006 }, + { 0x0981d4c5, 0x1400000a }, + { 0x0981d4d0, 0x24000019 }, + { 0x0981d4ea, 0x14000019 }, + { 0x0981d504, 0x24000001 }, + { 0x0981d507, 0x24000003 }, + { 0x0981d50d, 0x24000007 }, + { 0x0981d516, 0x24000006 }, + { 0x0981d51e, 0x14000019 }, + { 0x0981d538, 0x24000001 }, + { 0x0981d53b, 0x24000003 }, + { 0x0981d540, 0x24000004 }, + { 0x0901d546, 0x24000000 }, + { 0x0981d54a, 0x24000006 }, + { 0x0981d552, 0x14000019 }, + { 0x0981d56c, 0x24000019 }, + { 0x0981d586, 0x14000019 }, + { 0x0981d5a0, 0x24000019 }, + { 0x0981d5ba, 0x14000019 }, + { 0x0981d5d4, 0x24000019 }, + { 0x0981d5ee, 0x14000019 }, + { 0x0981d608, 0x24000019 }, + { 0x0981d622, 0x14000019 }, + { 0x0981d63c, 0x24000019 }, + { 0x0981d656, 0x14000019 }, + { 0x0981d670, 0x24000019 }, + { 0x0981d68a, 0x1400001b }, + { 0x0981d6a8, 0x24000018 }, + { 0x0901d6c1, 0x64000000 }, + { 0x0981d6c2, 0x14000018 }, + { 0x0901d6db, 0x64000000 }, + { 0x0981d6dc, 0x14000005 }, + { 0x0981d6e2, 0x24000018 }, + { 0x0901d6fb, 0x64000000 }, + { 0x0981d6fc, 0x14000018 }, + { 0x0901d715, 0x64000000 }, + { 0x0981d716, 0x14000005 }, + { 0x0981d71c, 0x24000018 }, + { 0x0901d735, 0x64000000 }, + { 0x0981d736, 0x14000018 }, + { 0x0901d74f, 0x64000000 }, + { 0x0981d750, 0x14000005 }, + { 0x0981d756, 0x24000018 }, + { 0x0901d76f, 0x64000000 }, + { 0x0981d770, 0x14000018 }, + { 0x0901d789, 0x64000000 }, + { 0x0981d78a, 0x14000005 }, + { 0x0981d790, 0x24000018 }, + { 0x0901d7a9, 0x64000000 }, + { 0x0981d7aa, 0x14000018 }, + { 0x0901d7c3, 0x64000000 }, + { 0x0981d7c4, 0x14000005 }, + { 0x0981d7ce, 0x34000031 }, + { 0x16820000, 0x1c00a6d6 }, + { 0x1682f800, 0x1c00021d }, + { 0x090e0001, 0x04000000 }, + { 0x098e0020, 0x0400005f }, + { 0x1b8e0100, 0x300000ef }, + { 0x098f0000, 0x0c00fffd }, + { 0x09900000, 0x0c00fffd }, +}; diff --git a/runtime/tools/benchmark.py b/runtime/tools/benchmark.py new file mode 100755 index 00000000000..b3a587fb58b --- /dev/null +++ b/runtime/tools/benchmark.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +# Simple wrapper for running benchmarks. + +import getopt +import optparse +import os +from os.path import join, dirname, realpath, abspath +import subprocess +import sys +import utils +import re + + +HOST_OS = utils.GuessOS() +HOST_CPUS = utils.GuessCpus() + +# Returns whether 'bench' matches any element in the 'filt' list. +def match(bench, filt): + bench = bench.lower(); + for element in filt: + if element.search(bench): + return True + return False + +def GetBenchmarkFile(path): + benchmark_root_path = [dirname(sys.argv[0]), '..', '..'] + ['benchmarks'] + return realpath(os.path.sep.join(benchmark_root_path + path)) + +def ReadBenchmarkList(mode, path, core): + filename = GetBenchmarkFile([path]) + benchmarks = dict() + execfile(filename, benchmarks) + if (mode == "release") and not core: + return benchmarks['SUPPORTED_BENCHMARKS'] + else: + return benchmarks['SUPPORTED_CORE_BENCHMARKS'] + +def BuildOptions(): + result = optparse.OptionParser() + result.add_option("-m", "--mode", + help='Build variants (comma-separated).', + metavar='[all,debug,release]', + default='release') + result.add_option("-v", "--verbose", + help='Verbose output.', + default=False, action="store_true") + result.add_option("-c", "--core", + help='Run only core benchmarks.', + default=False, action="store_true") + result.add_option("--arch", + help='Target architectures (comma-separated).', + metavar='[all,ia32,x64,simarm,arm,dartc]', + default=utils.GuessArchitecture()) + result.add_option("--executable", + help='Virtual machine to execute.', + metavar='[dart, (path to dart binary)]', + default=None) + result.add_option("-w", "--warmup", + help='Only run the warmup period.', + default=False, action="store_true") + return result + + +def ProcessOptions(options): + if options.arch == 'all': + options.arch = 'ia32,x64,simarm,dartc' + if options.mode == 'all': + options.mode = 'debug,release' + options.mode = options.mode.split(',') + options.arch = options.arch.split(',') + for mode in options.mode: + if not mode in ['debug', 'release']: + print "Unknown mode %s" % mode + return False + for arch in options.arch: + if not arch in ['ia32', 'x64', 'simarm', 'arm', 'dartc']: + print "Unknown arch %s" % arch + return False + return True + + +def GetBuildRoot(mode, arch): + return utils.GetBuildRoot(HOST_OS, mode, arch) + +def GetDart(mode, arch): + executable = [abspath(join(GetBuildRoot(mode, arch), 'dart'))] + return executable + +def Main(): + # Parse the options. + parser = BuildOptions() + (options, args) = parser.parse_args() + if not ProcessOptions(options): + parser.print_help() + return 1 + + chosen_benchmarks = ReadBenchmarkList(options.mode, + 'BENCHMARKS', + options.core) + + # Use arguments to filter the benchmarks. + if len(args) > 0: + filt = [re.compile(x.lower()) for x in args] + chosen_benchmarks = [b for b in chosen_benchmarks if match(b[0], filt)] + + for mode in options.mode: + for arch in options.arch: + if options.executable is None: + # Construct the path to the dart binary. + executable = GetDart(mode, arch) + else: + executable = [options.executable] + for benchmark, vmargs, progargs in chosen_benchmarks: + command = executable + command = command + [ + GetBenchmarkFile([benchmark, 'dart', benchmark + '.dart']), + ] + if options.verbose: + print ' '.join(command) + subprocess.call(command) + return 0 + + +if __name__ == '__main__': + sys.exit(Main()) diff --git a/runtime/tools/create_snapshot_file.py b/runtime/tools/create_snapshot_file.py new file mode 100755 index 00000000000..c9900b6c727 --- /dev/null +++ b/runtime/tools/create_snapshot_file.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +# Script to create snapshot files. + +import getopt +import optparse +import string +import subprocess +import sys +import utils + + +HOST_OS = utils.GuessOS() +HOST_CPUS = utils.GuessCpus() + + +def BuildOptions(): + result = optparse.OptionParser() + result.add_option("--executable", + action="store", type="string", + help="path to executable") + result.add_option("--output_bin", + action="store", type="string", + help="binary snapshot output file name") + result.add_option("--input_cc", + action="store", type="string", + help="input template file name") + result.add_option("--output", + action="store", type="string", + help="generated snapshot output file name") + result.add_option("--scripts", + action="store", type="string", + help="list of scripts to include in snapshot") + result.add_option("-v", "--verbose", + help='Verbose output.', + default=False, action="store_true") + return result + + +def ProcessOptions(options): + if not options.executable: + sys.stderr.write('--executable not specified\n') + return False + if not options.output_bin: + sys.stderr.write('--output_bin not specified\n') + return False + if not options.input_cc: + sys.stderr.write('--input_cc not specified\n') + return False + if not options.output: + sys.stderr.write('--output not specified\n') + return False + return True + + +def makeString(input_file): + result = ' ' + fileHandle = open(input_file, 'rb') + lineCounter = 0 + for byte in fileHandle.read(): + result += ' %d,' % ord(byte) + lineCounter += 1 + if lineCounter == 10: + result += '\n ' + lineCounter = 0 + if lineCounter != 0: + result += '\n ' + return result + + +def makeFile(output_file, input_cc_file, input_file): + snapshot_cc_text = open(input_cc_file).read() + snapshot_cc_text = snapshot_cc_text % makeString(input_file) + open(output_file, 'w').write(snapshot_cc_text) + return True + + +def Main(): + # Parse the options. + parser = BuildOptions() + (options, args) = parser.parse_args() + if not ProcessOptions(options): + parser.print_help() + return 1 + + # Construct the path to the dart binary. + snapshot_argument = ''.join([ "--snapshot=", options.output_bin ]) + if not options.scripts: + command = [ options.executable, snapshot_argument ] + else: + scripts = string.split(options.scripts) + command = [ options.executable, snapshot_argument, "--" ] + scripts + [ "--" ] + if options.verbose: + print ' '.join(command) + subprocess.call(command) + + if not makeFile(options.output, options.input_cc, options.output_bin): + return -1 + + return 0 + +if __name__ == '__main__': + sys.exit(Main()) diff --git a/runtime/tools/create_string_literal.py b/runtime/tools/create_string_literal.py new file mode 100644 index 00000000000..9f752e26e94 --- /dev/null +++ b/runtime/tools/create_string_literal.py @@ -0,0 +1,78 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. +# +# This python script creates a string literal in a C++ source file from a C++ +# source template and text file. + +import os +import sys +from os.path import join +import time +from optparse import OptionParser + + +def makeString(input_files): + result = ' ' + for string_file in input_files: + if string_file.endswith('dart'): + fileHandle = open(string_file, 'rb') + lineCounter = 0 + result += ' // ' + string_file + '\n ' + for byte in fileHandle.read(): + result += ' %d,' % ord(byte) + lineCounter += 1 + if lineCounter == 10: + result += '\n ' + lineCounter = 0 + if lineCounter != 0: + result += '\n ' + result += ' // Terminating null character.\n 0' + return result + + +def makeFile(output_file, input_cc_file, input_files): + bootstrap_cc_text = open(input_cc_file).read() + bootstrap_cc_text = bootstrap_cc_text % makeString(input_files) + open(output_file, 'w').write(bootstrap_cc_text) + return True + + +def main(args): + try: + # Parse input. + parser = OptionParser() + parser.add_option("--output", + action="store", type="string", + help="output file name") + parser.add_option("--input_cc", + action="store", type="string", + help="input template file") + + (options, args) = parser.parse_args() + if not options.output: + sys.stderr.write('--output not specified\n') + return -1 + if not len(options.input_cc): + sys.stderr.write('--input_cc not specified\n') + return -1 + if len(args) == 0: + sys.stderr.write('No input files specified\n') + return -1 + + files = [ ] + for arg in args: + files.append(arg) + + if not makeFile(options.output, options.input_cc, files): + return -1 + + return 0 + except Exception, inst: + sys.stderr.write('create_string_literal.py exception\n') + sys.stderr.write(str(inst)) + sys.stderr.write('\n') + return -1 + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/runtime/tools/gdb-macros b/runtime/tools/gdb-macros new file mode 100644 index 00000000000..019251c7101 --- /dev/null +++ b/runtime/tools/gdb-macros @@ -0,0 +1,624 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. +# +# Set of GDB macros that aid in debugging dart internal data structures +# in a non intrusive manner (no need to execute code and hence can be +# used in a corefile or after a crash when debugging inside gdb). +# +# include this file into your gdb session using "source tools/gdb-macros" +# The command "help user-defined" dumps all the extended commands available +# for debugging the dart VM once this file is included. +# + +# +# ============== +# Generic macros +# ============== +# +define bi + set $SHOW_CONTEXT = 1 + break * $arg0 +end +document bi +Set a breakpoint on address +Usage: bi addr +end + +# Enable break points. +define be + enable $arg0 +end +document be +Enable breakpoint # +Usage: be num +end + +# Disable break points. +define bd + disable $arg0 +end +document bd +Disable breakpoint # +Usage: bd num +end + +# Print frame information. +define stack + info frame + info stack + info args + info locals +end +document stack +Print stack details +end + + +# +# ==================================== +# Object type and contents dump macros +# ==================================== +# +define printrawtype + if $argc == 1 + set $raw_object = (uword)$arg0 + if ($raw_object & dart::kSmiTagMask) == dart::kHeapObjectTag + set $raw_object = $raw_object - dart::kHeapObjectTag + set $raw_class = (uword)(((dart::RawObject*)$raw_object)->class_) - dart::kHeapObjectTag + set $type = ((dart::RawClass*)$raw_class)->instance_kind_ + print $type + else + printf "dart::kSmi\n" + end + else + printf "Usage: printrawtype
\n" + end +end +document printrawtype +Print the type of specified raw object +end + +define printtype + if $argc == 1 + set $handle = $arg0 + printrawtype $handle.raw_ + else + printf "Usage: printtype \n" + end +end +document printtype +Print the type of object pointed to by the handle +end + +define printname + if $argc == 1 + # TODO(asiva): Need to deal with generic strings. + set $raw_onebytestring = (dart::RawOneByteString*)((uword)$arg0 - dart::kHeapObjectTag) + set $length = (uword)($raw_onebytestring->length_) >> dart::kSmiTagMask + set $offset = sizeof(dart::RawOneByteString) + while $length > 0 + printf "%c", *((char*)((uword)$raw_onebytestring + $offset)) + set $offset = $offset + 1 + set $length = $length - 1 + end + printf "\n" + else + printf "Null\n" + end +end + +define printfunctions + if $argc == 1 + set $raw_array = (dart::RawArray*)((uword)$arg0 - dart::kHeapObjectTag) + set $index = 0 + while $index < (uword)$raw_array->length_ >> dart::kSmiTagMask + set $temp = *(dart::RawObject*)((uword)$raw_array + \ + sizeof(dart::RawArray) + \ + ($index * sizeof(dart::RawObject*))) + set $temp = (uword)$temp - dart::kHeapObjectTag + printrawfunction $temp + set $index = $index + 1 + end + else + printf "Null\n" + end +end + +define printfields + if $argc == 1 + set $raw_array = (dart::RawArray*)((uword)$arg0 - dart::kHeapObjectTag) + set $index = 0 + while $index < (uword)$raw_array->length_ >> dart::kSmiTagMask + set $temp = *(dart::RawObject*)((uword)$raw_array + \ + sizeof(dart::RawArray) + \ + ($index * sizeof(dart::RawObject*))) + set $temp = (uword)$temp - dart::kHeapObjectTag + printrawfield $temp + set $index = $index + 1 + end + else + printf "Null\n" + end +end + +define printinterfaces + if $argc == 1 + set $raw_array = (dart::RawArray*)((uword)$arg0 - dart::kHeapObjectTag) + set $index = 0 + while $index < (uword)$raw_array->length_ >> dart::kSmiTagMask + set $temp = *(dart::RawObject*)((uword)$raw_array + \ + sizeof(dart::RawArray) + \ + ($index * sizeof(dart::RawObject*))) + set $raw_class = (dart::RawClass*)((uword)$temp - dart::kHeapObjectTag) + printname $raw_class->name_ + set $index = $index + 1 + end + else + printf "Null\n" + end +end + +define printrawclazz + if $argc == 1 + set $raw_class = (dart::RawClass*)$arg0 + set $is_interface = (uword)$raw_class->is_interface_ + if ($is_interface) + printf "Interface Name : " + else + printf "Class Name : " + end + printname $raw_class->name_ + printf "\nMethods :\n" + printfunctions $raw_class->functions_ + printf "\nFields :\n" + printfields $raw_class->fields_ + printf "\nInterfaces :\n" + printinterfaces $raw_class->interfaces_ + set $raw_closure_function = (uword)$raw_class->closure_function_ + if ($raw_closure_function != dart::Object::null()) + printf "\nClosure Function :\n" + printrawfunction $raw_closure_function + end + printf "\n" + else + printf "Usage: printrawclazz
\n" + end +end +document printrawclazz +Print contents of RawClass +end + +define printrawfunction + if $argc == 1 + set $raw_function = (dart::RawFunction*)$arg0 + printf "Name : " + printname $raw_function->name_ + set $raw_code = (uword)$raw_function->code_ + printf "Code : \n" + if ($raw_code == dart::Object::null()) + printf " not been generated yet\n" + else + printrawcode $raw_code + end + else + printf "Usage: printrawfunction
\n" + end +end +document printrawfunction +Print contents of RawFunction +end + +define printrawfield + if $argc == 1 + set $raw_field = (dart::RawField*)$arg0 + printf "Name : " + printname $raw_field->name_ + printf "Value : " + printrawobject (uword)$raw_field->value_ + else + printf "Usage: printrawfield
\n" + end +end +document printrawfield +Print contents of RawField +end + +define printrawscript + if $argc == 1 + set $raw_script = (dart::RawScript*)$arg0 + printf "printrawscript is not implemented yet\n" + else + printf "Usage: printrawscript
\n" + end +end +document printrawscript +Print contents of RawScript +end + +define printrawcode + if $argc == 1 + set $raw_code = (dart::RawCode*)$arg0 + set $instrs = (uword)$raw_code->instructions_ - dart::kHeapObjectTag + printrawinstructions $instrs + set $start = (uword)$raw_code + sizeof(dart::RawCode) + printf "Pointer Offsets :\n" + x/x $start, $start + $raw_code->pointer_offsets_length_ + else + printf "Usage: printrawcode
\n" + end +end +document printrawcode +Print contents of RawCode +end + +define printrawinstructions + if $argc == 1 + set $raw_instrs = (dart::RawInstructions*)$arg0 + set $size = $raw_instrs->size_ + set $alignment = 16 + set $headersize = (sizeof(dart::RawInstructions) + $alignment -1) & -$alignment + set $start = (uword)$raw_instrs + \ + (($headersize + $alignment - 1) & -$alignment) + printf "code_ = %p\n", $raw_instrs->code_ + printf "size_ = %d\n", $raw_instrs->size_ + disassemble $start, $start + ($size - $headersize) + else + printf "Usage: printrawinstructions
\n" + end +end +document printrawinstructions +Print contents of RawInstructions +end + +define printrawinstance + if $argc == 1 + set $raw_instance = (dart::RawInstance*)$arg0 + set $raw_class = (uword)((dart::RawObject*)$raw_instance)->class_ - dart::kHeapObjectTag + printf "Instance of class " + printname ((dart::RawClass*)$raw_class)->name_ + else + printf "Usage: printrawinstance
\n" + end +end +document printrawinstance +Print contents of RawInstance +end + +define printrawinteger + if $argc == 1 + set $raw_integer = (dart::RawInteger*)$arg0 + printf "printrawinteger is not implemented yet\n" + else + printf "Usage: printrawinteger
\n" + end +end +document printrawinteger +Print contents of RawInteger +end + +define printrawdouble + if $argc == 1 + set $raw_double = (dart::RawDouble*)$arg0 + printf "printrawdouble is not implemented yet\n" + else + printf "Usage: printrawdouble
\n" + end +end +document printrawdouble +Print contents of RawDouble +end + +define printrawstring + if $argc == 1 + set $raw_string = (dart::RawString*)$arg0 + printf "printrawstring is not implemented yet\n" + else + printf "Usage: printrawstring
\n" + end +end +document printrawstring +Print contents of RawString +end + +define printrawonebytestring + if $argc == 1 + set $raw_onebytestring = (dart::RawOneByteString*)$arg0 + set $length = (uword)($raw_onebytestring->length_) >> dart::kSmiTagMask + printf "Length = %d\n", $length + printf "Hash = %d\n", $raw_onebytestring->hash_ + set $offset = sizeof(dart::RawOneByteString) + while $length > 0 + printf "%c", *((char*)((uword)$raw_onebytestring + $offset)) + set $offset = $offset + 1 + set $length = $length - 1 + end + printf "\n" + else + printf "Usage: printrawonebytestring
\n" + end +end +document printrawonebytestring +Print contents of RawString +end + +# TODO(asiva): This function can be called recursively when we are +# printing other objects so we need to save and restore variables. +define printrawarray + if $argc == 1 + set $raw_array = (dart::RawArray*)$arg0 + printf "Array Length = %d\n", (uword)$raw_array->length_ >> dart::kSmiTagMask + set $index = 0 + while $index < (uword)$raw_array->length_ >> dart::kSmiTagMask + printf "Element %d:\n", $index + set $temp = *(dart::RawObject*)((uword)$raw_array + \ + sizeof(dart::RawArray) + \ + ($index * sizeof(dart::RawObject*))) + printrawobject $temp + set $index = $index + 1 + end + else + printf "Usage: printrawarray
\n" + end +end +document printrawarray +Print contents of RawArray +end + +define printrawclosure + if $argc == 1 + set $raw_closure = (dart::RawClosure*)$arg0 + printf "class_ = %p\n", $raw_closure->class_ + printf "context_ = %p\n", $raw_closure->context_ + else + printf "Usage: printrawclosure
\n" + end +end +document printrawclosure +Print contents of RawClosure +end + +define printrawcontext + if $argc == 1 + set $raw_context = (dart::RawContext*)$arg0 + printf "parent_ = %p\n", $raw_context->parent_ + printf "isolate_ = %p\n", $raw_context->isolate_ + printf "num_variables_ = %d\n", $raw_context->num_variables_ + set $index = 0 + while $index < (uword)$raw_context->num_variables_ + printf "Variable %d:\n", $index + set $temp = *(dart::RawObject*)((uword)$raw_context + \ + sizeof(dart::RawContext) + \ + ($index * sizeof(dart::RawObject*))) + printrawobject $temp + set $index = $index + 1 + end + else + printf "Usage: printrawcontext
\n" + end +end +document printrawcontext +Print contents of RawContext +end + +define printrawcontextscope + if $argc == 1 + set $raw_context_scope = (dart::RawContext*)$arg0 + printf "num_variables_ = %d\n", $raw_context_scope->num_variables_ + else + printf "Usage: printrawcontextscope
\n" + end +end +document printrawcontextscope +Print contents of RawContextScope +end + +# TODO(asiva): This function can be called recursively when we are +# printing other objects so we need to save and restore variables. +define printrawobject + if $argc == 1 + set $raw_object = (uword)$arg0 + if ($raw_object & dart::kSmiTagMask) == dart::kHeapObjectTag + set $raw_object = (uword)$raw_object - dart::kHeapObjectTag + set $raw_class = (uword)((dart::RawObject*)$raw_object)->class_ - dart::kHeapObjectTag + set $type = ((dart::RawClass*)$raw_class)->instance_kind_ + set $type_found = false + if $type == dart::kClass + printrawclazz $raw_object + set $type_found = true + end + if $type == dart::kFunction + printrawfunction $raw_object + set $type_found = true + end + if $type == dart::kScript + printrawscript $raw_object + set $type_found = true + end + if $type == dart::kCode + printrawcode $raw_object + set $type_found = true + end + if $type == dart::kInstructions + printrawinstructions $raw_object + set $type_found = true + end + if $type == dart::kInstance + printrawinstance $raw_object + set $type_found = true + end + if $type == dart::kNumber + printf "dump of RawNumber is not implemented yet\n" + set $type_found = true + end + if $type == dart::kInteger + printrawinteger $raw_object + set $type_found = true + end + if $type == dart::kDouble + printrawdouble $raw_object + set $type_found = true + end + if $type == dart::kString + printrawstring $raw_object + set $type_found = true + end + if $type == dart::kOneByteString + printrawonebytestring $raw_object + set $type_found = true + end + if $type == dart::kArray + printrawarray $raw_object + set $type_found = true + end + if $type == dart::kClosure + printrawclosure $raw_object + set $type_found = true + end + if $type == dart::kContext + printrawcontext $raw_object + set $type_found = true + end + if $type == dart::kContextScope + printrawcontextscope $raw_object + set $type_found = true + end + if $type_found == false + printf "unknown type %d\n", $type + end + else + print (uword)$raw_object >> dart::kSmiTagMask + end + else + printf "Usage: printrawobject
\n" + end +end +document printrawobject +Print contents of specified raw object +end + +define printobject + if $argc == 1 + set $handle = $arg0 + set $raw_object = $handle.raw_ + printrawobject $raw_object + else + printf "Usage: printobject \n" + end +end +document printobject +Print contents of object pointed to by the handle +end + + +# +# ================================= +# Zone and Handle monitoring macros +# ================================= +# + + +# +# =================== +# Generic dump macros +# =================== +# +# Print as ascii characters. +define ascii_char + set $_c=*(unsigned char *)($arg0) + if ( $_c < 0x20 || $_c > 0x7E ) + printf "." + else + printf "%c", $_c + end +end +document ascii_char +Print the ASCII value of arg0 or '.' if value is unprintable +end + +# Hex dump (eight individual characters). +define hex_quad + printf "%02X %02X %02X %02X %02X %02X %02X %02X", \ + *(unsigned char*)($arg0), *(unsigned char*)($arg0 + 1), \ + *(unsigned char*)($arg0 + 2), *(unsigned char*)($arg0 + 3), \ + *(unsigned char*)($arg0 + 4), *(unsigned char*)($arg0 + 5), \ + *(unsigned char*)($arg0 + 6), *(unsigned char*)($arg0 + 7) +end +document hex_quad +Print eight hexadecimal bytes starting at arg0 +end + +# 16 byte hex dump. +define hexdump + printf "%08X : ", $arg0 + hex_quad $arg0 + printf " - " + hex_quad ($arg0+8) + printf " " + ascii_char ($arg0) + ascii_char ($arg0+1) + ascii_char ($arg0+2) + ascii_char ($arg0+3) + ascii_char ($arg0+4) + ascii_char ($arg0+5) + ascii_char ($arg0+6) + ascii_char ($arg0+7) + ascii_char ($arg0+8) + ascii_char ($arg0+9) + ascii_char ($arg0+0xA) + ascii_char ($arg0+0xB) + ascii_char ($arg0+0xC) + ascii_char ($arg0+0xD) + ascii_char ($arg0+0xE) + ascii_char ($arg0+0xF) + printf "\n" +end +document hexdump +Display a 16-byte hex/ASCII dump of arg0 +end + +# data dump. +define ddump + printf "[%04X:%08X]------------------------", $ds, $data_addr + printf "---------------------------------[ data]\n" + set $_count=0 + while ( $_count < $arg0 ) + set $_i=($_count*0x10) + hexdump ($data_addr+$_i) + set $_count++ + end +end +document ddump +Display $arg0 lines of hexdump for address $data_addr +end + +define dd + set $data_addr=$arg0 + ddump 0x10 +end +document dd +Display 16 lines of a hex dump for $arg0 +end + +define ddx + x/10x $arg0 +end +document ddx +Display 16 lines of a hex dump for $arg0 +end + +define ddi + x/10i $arg0 +end +document ddi +Display 16 lines of a instructions for $arg0 +end + + +# +# =============== +# Init parameters +# =============== +# +set print pretty on +#set disassembly-flavor intel diff --git a/runtime/tools/generate_projects.py b/runtime/tools/generate_projects.py new file mode 100755 index 00000000000..ac4efed3dd6 --- /dev/null +++ b/runtime/tools/generate_projects.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +import os +import sys + +# Change into the dart directory as we want the project to be rooted here. +runtime_src = os.path.join(os.path.dirname(sys.argv[0]), os.pardir) +gclient_src = os.path.join(runtime_src, os.pardir) +project_src = os.path.join(gclient_src, sys.argv[1]) +os.chdir(project_src) + +# Add gyp to the imports and if needed get it from the third_party location +# inside the standalone dart gclient checkout. +try: + import gyp +except ImportError, e: + sys.path.append(os.path.join(os.pardir, 'third_party', 'gyp', 'pylib')) + import gyp + +if __name__ == '__main__': + args = ['--depth', '.'] + args += ['dart-runtime.gyp'] + + # Generate the projects. + sys.exit(gyp.main(args)) diff --git a/runtime/tools/gyp/runtime-configurations.gypi b/runtime/tools/gyp/runtime-configurations.gypi new file mode 100644 index 00000000000..b5dc86002da --- /dev/null +++ b/runtime/tools/gyp/runtime-configurations.gypi @@ -0,0 +1,16 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +{ + 'target_defaults': { + 'configurations': { + 'Dart_Debug': { + 'abstract': 1, + 'defines': [ + 'DEBUG', + ], + }, + }, + }, +} diff --git a/runtime/tools/utils.py b/runtime/tools/utils.py new file mode 100644 index 00000000000..bc3fe478c83 --- /dev/null +++ b/runtime/tools/utils.py @@ -0,0 +1,148 @@ +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +# This file contains a set of utilities functions used by other Python-based +# scripts. + +import platform +import re +import os +import commands + + +# Try to guess the host operating system. +def GuessOS(): + id = platform.system() + if id == "Linux": + return "linux" + elif id == "Darwin": + return "macos" + elif id == "Windows" or id == "Microsoft": + # On Windows Vista platform.system() can return "Microsoft" with some + # versions of Python, see http://bugs.python.org/issue1082 for details. + return "win32" + elif id == 'FreeBSD': + return 'freebsd' + elif id == 'OpenBSD': + return 'openbsd' + elif id == 'SunOS': + return 'solaris' + else: + return None + + +# Try to guess the host architecture. +def GuessArchitecture(): + id = platform.machine() + if id.startswith('arm'): + return 'arm' + elif (not id) or (not re.match('(x|i[3-6])86', id) is None): + return 'ia32' + elif id == 'i86pc': + return 'ia32' + else: + return None + + +# Try to guess the number of cpus on this machine. +def GuessCpus(): + if os.path.exists("/proc/cpuinfo"): + return int(commands.getoutput("grep -E '^processor' /proc/cpuinfo | wc -l")) + if os.path.exists("/usr/bin/hostinfo"): + return int(commands.getoutput('/usr/bin/hostinfo | grep "processors are logically available." | awk "{ print \$1 }"')) + win_cpu_count = os.getenv("NUMBER_OF_PROCESSORS") + if win_cpu_count: + return int(win_cpu_count) + return int(os.getenv("DART_NUMBER_OF_CORES", 2)) + + +# Returns true if we're running under Windows. +def IsWindows(): + return GuessOS() == 'win32' + + +# Reads a text file into an array of strings - one for each +# line. Strips comments in the process. +def ReadLinesFrom(name): + result = [] + for line in open(name): + if '#' in line: + line = line[:line.find('#')] + line = line.strip() + if len(line) == 0: + continue + result.append(line) + return result + +# Filters out all arguments until the next '--' argument +# occurs. +def ListArgCallback(option, opt_str, value, parser): + if value is None: + value = [] + + for arg in parser.rargs: + if arg[:2].startswith('--'): + break + value.append(arg) + + del parser.rargs[:len(value)] + setattr(parser.values, option.dest, value) + + +# Filters out all argument until the first non '-' or the +# '--' argument occurs. +def ListDashArgCallback(option, opt_str, value, parser): + if value is None: + value = [] + + for arg in parser.rargs: + if arg[:2].startswith('--') or arg[0] != '-': + break + value.append(arg) + + del parser.rargs[:len(value)] + setattr(parser.values, option.dest, value) + + +# Mapping table between build mode and build configuration. +BUILD_MODES = { + 'debug': 'Debug', + 'release': 'Release', +} + + +# Mapping table between OS and build output location. +BUILD_ROOT = { + 'linux': os.path.join('out'), + 'freebsd': os.path.join('out'), + 'macos': os.path.join('xcodebuild'), +} + +def GetBuildMode(mode): + global BUILD_MODES + return BUILD_MODES[mode] + + +def GetBuildConf(mode, arch): + return GetBuildMode(mode) + "_" + arch + + +def GetBuildRoot(target_os, mode=None, arch=None): + global BUILD_ROOT + if mode: + return os.path.join(BUILD_ROOT[target_os], GetBuildConf(mode, arch)) + else: + return BUILD_ROOT[target_os] + + +def Main(argv): + print "GuessOS() -> ", GuessOS() + print "GuessArchitecture() -> ", GuessArchitecture() + print "GuessCpus() -> ", GuessCpus() + print "IsWindows() -> ", IsWindows() + + +if __name__ == "__main__": + import sys + Main(sys.argv) diff --git a/runtime/tools/valgrind.py b/runtime/tools/valgrind.py new file mode 100755 index 00000000000..c0440f17485 --- /dev/null +++ b/runtime/tools/valgrind.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# +# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +# Simple wrapper for running Valgrind and checking the output on +# stderr for memory leaks. + +import subprocess +import sys +import re + +VALGRIND_ARGUMENTS = [ + 'valgrind', + '--error-exitcode=1', + '--leak-check=full', + '--trace-children=yes', + '--ignore-ranges=0x000-0xFFF', # Used for implicit null checks. + '--vex-iropt-level=1' # Valgrind crashes with the default level (2). +] + +# Compute the command line. +command = VALGRIND_ARGUMENTS + sys.argv[1:] + +# Run Valgrind. +process = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) +code = process.wait() +output = process.stdout.readlines() +errors = process.stderr.readlines() + +# Always print the output, but leave out the 3 line banner printed +# by certain versions of Valgrind. +if len(output) > 0 and output[0].startswith("** VALGRIND_ROOT="): + output = output[3:] +sys.stdout.writelines(output) + +# If Valgrind produced an error, we report that to the user. +if code != 0: + sys.stderr.writelines(errors) + sys.exit(code) + +# Look through the leak details and make sure that we don't have +# any definitely or indirectly lost bytes. We allow possibly lost +# bytes to lower the risk of false positives. +LEAK_RE = r"(?:definitely|indirectly) lost:" +LEAK_LINE_MATCHER = re.compile(LEAK_RE) +LEAK_OKAY_MATCHER = re.compile(r"lost: 0 bytes in 0 blocks") +leaks = [] +for line in errors: + if LEAK_LINE_MATCHER.search(line): + leaks.append(line) + if not LEAK_OKAY_MATCHER.search(line): + sys.stderr.writelines(errors) + sys.exit(1) + +# Make sure we found the right number of leak lines. +if not len(leaks) in [0, 2, 3]: + sys.stderr.writelines(errors) + sys.stderr.write('\n\n#### Malformed Valgrind output.\n#### Exiting.\n') + sys.exit(1) + +# Success. +sys.exit(0) diff --git a/runtime/vm/allocation.cc b/runtime/vm/allocation.cc new file mode 100644 index 00000000000..592a48bf28b --- /dev/null +++ b/runtime/vm/allocation.cc @@ -0,0 +1,38 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/allocation.h" + +#include "vm/assert.h" +#include "vm/isolate.h" +#include "vm/zone.h" + +namespace dart { + +StackResource::StackResource() { + Isolate* isolate = Isolate::Current(); + previous_ = isolate->top_resource(); + isolate->set_top_resource(this); +} + + +StackResource::~StackResource() { + Isolate* isolate = Isolate::Current(); + StackResource* top = isolate->top_resource(); + ASSERT(top == this); + isolate->set_top_resource(previous_); +} + +ZoneAllocated::~ZoneAllocated() { + UNREACHABLE(); +} + +void* ZoneAllocated::operator new(uword size) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ASSERT(isolate->current_zone() != NULL); + return reinterpret_cast(isolate->current_zone()->Allocate(size)); +} + +} // namespace dart diff --git a/runtime/vm/allocation.h b/runtime/vm/allocation.h new file mode 100644 index 00000000000..4c7b5fca2ff --- /dev/null +++ b/runtime/vm/allocation.h @@ -0,0 +1,89 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_ALLOCATION_H_ +#define VM_ALLOCATION_H_ + +#include "vm/assert.h" + +namespace dart { + +// Stack allocated objects subclass from this base class. Objects of this type +// cannot be allocated on either the C or object heaps. Destructors for objects +// of this type will not be run unless the stack is unwound through normal +// program control flow. +class ValueObject { + public: + ValueObject() { } + ~ValueObject() { } + + private: + DISALLOW_ALLOCATION(); + // TODO(5411081): Add DISALLOW_COPY_AND_ASSIGN(ValueObject) once the mac + // build issue is resolved. +}; + + +// Stack resources subclass from this base class. The VM will ensure that the +// destructors of these objects are called before the stack is unwound past the +// objects location on the stack. Use stack resource objects if objects +// need to be destroyed even in the case of exceptions when a Longjump is done +// to a stack frame above the frame where these objects were allocated. +class StackResource { + public: + StackResource(); + virtual ~StackResource(); + + // The delete operator should be private instead of public, but unfortunately + // the compiler complains when compiling the destructors for subclasses. + void operator delete(void* pointer) { UNREACHABLE(); } + + private: + StackResource* previous_; + + void* operator new(uword size); + + DISALLOW_COPY_AND_ASSIGN(StackResource); +}; + + +// Static allocated classes only contain static members and can never +// be instantiated in the heap or on the stack. +class AllStatic { + private: + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(AllStatic); +}; + + +// Zone allocated objects cannot be individually deallocated, but have +// to rely on the destructor of Zone which is called when the Zone +// goes out of scope to reclaim memory. +class ZoneAllocated { + public: + ZoneAllocated() { } + // It would be ideal if the destructor method could be made private, + // but the g++ compiler complains when this is subclassed. + virtual ~ZoneAllocated(); + + // Implicitly allocate the object in the current zone. + void* operator new(uword size); + + // Ideally, the delete operator should be protected instead of + // public, but unfortunately the compiler sometimes synthesizes + // (unused) destructors for classes derived from ZoneObject, which + // require the operator to be visible. MSVC requires the delete + // operator to be public. + + // Disallow explicit deallocation of nodes. Nodes can only be + // deallocated by invoking DeleteAll() on the zone they live in. + void operator delete(void* pointer) { UNREACHABLE(); } + + private: + DISALLOW_COPY_AND_ASSIGN(ZoneAllocated); +}; + +} // namespace dart + +#endif // VM_ALLOCATION_H_ diff --git a/runtime/vm/allocation_test.cc b/runtime/vm/allocation_test.cc new file mode 100644 index 00000000000..f8b4422118d --- /dev/null +++ b/runtime/vm/allocation_test.cc @@ -0,0 +1,157 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/allocation.h" +#include "vm/assert.h" +#include "vm/longjump.h" +#include "vm/unit_test.h" + +namespace dart { + +class TestValueObject : public ValueObject { + public: + explicit TestValueObject(int* ptr) : ptr_(ptr) { + EXPECT_EQ(1, *ptr_); + *ptr_ = 2; + } + + virtual ~TestValueObject() { + EXPECT_EQ(3, *ptr_); + *ptr_ = 4; + } + + int value() const { return *ptr_; } + virtual int GetId() const { return 3; } + + private: + int* ptr_; +}; + + +class TestStackResource : public StackResource { + public: + explicit TestStackResource(int* ptr) : ptr_(ptr) { + EXPECT_EQ(1, *ptr_); + *ptr_ = 2; + } + + ~TestStackResource() { + EXPECT_EQ(6, *ptr_); + *ptr_ = 7; + } + + int value() const { return *ptr_; } + virtual int GetId() const { return 3; } + + private: + int* ptr_; +}; + + +class TestStackedStackResource : public StackResource { + public: + explicit TestStackedStackResource(int* ptr) : ptr_(ptr) { + EXPECT_EQ(3, *ptr_); + *ptr_ = 4; + } + + ~TestStackedStackResource() { + EXPECT_EQ(5, *ptr_); + *ptr_ = 6; + } + + int value() const { return *ptr_; } + + private: + int* ptr_; +}; + + +static void StackAllocatedDestructionHelper(int* ptr) { + TestValueObject stacked(ptr); + EXPECT_EQ(2, *ptr); + *ptr = 3; +} + + +UNIT_TEST_CASE(StackAllocatedDestruction) { + int data = 1; + StackAllocatedDestructionHelper(&data); + EXPECT_EQ(4, data); +} + + +static void StackAllocatedLongJumpHelper(int* ptr, LongJump* jump) { + TestValueObject stacked(ptr); + EXPECT_EQ(2, *ptr); + *ptr = 3; + jump->Jump(1, "StackAllocatedLongJump Test"); + UNREACHABLE(); +} + + +TEST_CASE(StackAllocatedLongJump) { + LongJump jump; + int data = 1; + if (setjmp(*jump.Set()) == 0) { + StackAllocatedLongJumpHelper(&data, &jump); + UNREACHABLE(); + } + EXPECT_EQ(3, data); +} + + +static void StackedStackResourceDestructionHelper(int* ptr) { + TestStackedStackResource stacked(ptr); + EXPECT_EQ(4, *ptr); + *ptr = 5; +} + + +static void StackResourceDestructionHelper(int* ptr) { + TestStackResource stacked(ptr); + EXPECT_EQ(2, *ptr); + *ptr = 3; + StackedStackResourceDestructionHelper(ptr); + EXPECT_EQ(6, *ptr); + // Do not set data because the LongJump version does not return control here. +} + + +TEST_CASE(StackResourceDestruction) { + int data = 1; + StackResourceDestructionHelper(&data); + EXPECT_EQ(7, data); +} + + +static void StackedStackResourceLongJumpHelper(int* ptr, LongJump* jump) { + TestStackedStackResource stacked(ptr); + EXPECT_EQ(4, *ptr); + *ptr = 5; + jump->Jump(1, "StackResourceLongJump Test"); + UNREACHABLE(); +} + + +static void StackResourceLongJumpHelper(int* ptr, LongJump* jump) { + TestStackResource stacked(ptr); + EXPECT_EQ(2, *ptr); + *ptr = 3; + StackedStackResourceLongJumpHelper(ptr, jump); + UNREACHABLE(); +} + + +TEST_CASE(StackResourceLongJump) { + LongJump jump; + int data = 1; + if (setjmp(*jump.Set()) == 0) { + StackResourceLongJumpHelper(&data, &jump); + UNREACHABLE(); + } + EXPECT_EQ(7, data); +} + +} // namespace dart diff --git a/runtime/vm/assembler.cc b/runtime/vm/assembler.cc new file mode 100644 index 00000000000..6c989c4d11b --- /dev/null +++ b/runtime/vm/assembler.cc @@ -0,0 +1,169 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assembler.h" +#include "vm/cpu.h" +#include "vm/heap.h" +#include "vm/memory_region.h" +#include "vm/utils.h" +#include "vm/zone.h" + +namespace dart { + +static uword NewContents(int capacity) { + Zone* zone = Isolate::Current()->current_zone(); + uword result = zone->Allocate(capacity); +#if defined(DEBUG) + // Initialize the buffer with kBreakPointInstruction to force a break + // point if we ever execute an uninitialized part of the code buffer. + Assembler::InitializeMemoryWithBreakpoints(result, capacity); +#endif + return result; +} + + +#if defined(DEBUG) +AssemblerBuffer::EnsureCapacity::EnsureCapacity(AssemblerBuffer* buffer) { + if (buffer->cursor() >= buffer->limit()) buffer->ExtendCapacity(); + // In debug mode, we save the assembler buffer along with the gap + // size before we start emitting to the buffer. This allows us to + // check that any single generated instruction doesn't overflow the + // limit implied by the minimum gap size. + buffer_ = buffer; + gap_ = ComputeGap(); + // Make sure that extending the capacity leaves a big enough gap + // for any kind of instruction. + ASSERT(gap_ >= kMinimumGap); + // Mark the buffer as having ensured the capacity. + ASSERT(!buffer->HasEnsuredCapacity()); // Cannot nest. + buffer->has_ensured_capacity_ = true; +} + + +AssemblerBuffer::EnsureCapacity::~EnsureCapacity() { + // Unmark the buffer, so we cannot emit after this. + buffer_->has_ensured_capacity_ = false; + // Make sure the generated instruction doesn't take up more + // space than the minimum gap. + int delta = gap_ - ComputeGap(); + ASSERT(delta <= kMinimumGap); +} +#endif + + +AssemblerBuffer::AssemblerBuffer() + : pointer_offsets_(new ZoneGrowableArray(16)) { + static const int kInitialBufferCapacity = 4 * KB; + contents_ = NewContents(kInitialBufferCapacity); + cursor_ = contents_; + limit_ = ComputeLimit(contents_, kInitialBufferCapacity); + fixup_ = NULL; +#if defined(DEBUG) + has_ensured_capacity_ = false; + fixups_processed_ = false; +#endif + + // Verify internal state. + ASSERT(Capacity() == kInitialBufferCapacity); + ASSERT(Size() == 0); +} + + +AssemblerBuffer::~AssemblerBuffer() { +} + + +void AssemblerBuffer::ProcessFixups(const MemoryRegion& region) { + AssemblerFixup* fixup = fixup_; + while (fixup != NULL) { + fixup->Process(region, fixup->position()); + fixup = fixup->previous(); + } +} + + +void AssemblerBuffer::FinalizeInstructions(const MemoryRegion& instructions) { + // Copy the instructions from the buffer. + MemoryRegion from(reinterpret_cast(contents()), Size()); + instructions.CopyFrom(0, from); + + // Process fixups in the instructions. + ProcessFixups(instructions); +#if defined(DEBUG) + fixups_processed_ = true; +#endif +} + + +void AssemblerBuffer::ExtendCapacity() { + int old_size = Size(); + int old_capacity = Capacity(); + int new_capacity = Utils::Minimum(old_capacity * 2, old_capacity + 1 * MB); + + // Allocate the new data area and copy contents of the old one to it. + uword new_contents = NewContents(new_capacity); + memmove(reinterpret_cast(new_contents), + reinterpret_cast(contents_), + old_size); + + // Compute the relocation delta and switch to the new contents area. + intptr_t delta = new_contents - contents_; + contents_ = new_contents; + + // Update the cursor and recompute the limit. + cursor_ += delta; + limit_ = ComputeLimit(new_contents, new_capacity); + + // Verify internal state. + ASSERT(Capacity() == new_capacity); + ASSERT(Size() == old_size); +} + + +class PatchCodeWithHandle : public AssemblerFixup { + public: + PatchCodeWithHandle(ZoneGrowableArray* pointer_offsets, + const Object& object) + : pointer_offsets_(pointer_offsets), object_(object) { + } + + void Process(const MemoryRegion& region, int position) { + // Patch the handle into the code. Once the instructions are installed into + // a raw code object and the pointer offsets are setup, the handle is + // resolved. + region.Store(position, &object_); + pointer_offsets_->Add(position); + } + + private: + ZoneGrowableArray* pointer_offsets_; + const Object& object_; +}; + + +void AssemblerBuffer::EmitObject(const Object& object) { + // Since we are going to store the handle as part of the fixup information + // the handle needs to be a zone handle. + ASSERT(object.IsZoneHandle()); + EmitFixup(new PatchCodeWithHandle(pointer_offsets_, object)); + cursor_ += kWordSize; // Reserve space for pointer. +} + + +// Shared macros are implemented here. +void Assembler::Unimplemented(const char* message) { + Stop("unimplemented"); +} + + +void Assembler::Untested(const char* message) { + Stop("untested"); +} + + +void Assembler::Unreachable(const char* message) { + Stop("unreachable"); +} + +} // namespace dart diff --git a/runtime/vm/assembler.h b/runtime/vm/assembler.h new file mode 100644 index 00000000000..1919208e3ff --- /dev/null +++ b/runtime/vm/assembler.h @@ -0,0 +1,208 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_ASSEMBLER_H_ +#define VM_ASSEMBLER_H_ + +#include "vm/allocation.h" +#include "vm/assert.h" +#include "vm/globals.h" +#include "vm/growable_array.h" +#include "vm/object.h" + +namespace dart { + +// Forward declarations. +class Assembler; +class AssemblerFixup; +class AssemblerBuffer; +class MemoryRegion; + + +// External labels keep a function pointer to allow them +// to be called from code generated by the assembler. +class ExternalLabel : public ValueObject { + public: + + ExternalLabel(const char* name, uword address) + : name_(name), address_(address) { + ASSERT(name != NULL); + } + + const char* name() const { return name_; } + bool is_resolved() const { return address_ != 0; } + uword address() const { + ASSERT(is_resolved()); + return address_; + } + + private: + const char* name_; + const uword address_; +}; + + +// Assembler fixups are positions in generated code that hold relocation +// information that needs to be processed before finalizing the code +// into executable memory. +class AssemblerFixup : public ZoneAllocated { + public: + virtual void Process(const MemoryRegion& region, int position) = 0; + + // It would be ideal if the destructor method could be made private, + // but the g++ compiler complains when this is subclassed. + virtual ~AssemblerFixup() { UNREACHABLE(); } + + private: + AssemblerFixup* previous_; + int position_; + + AssemblerFixup* previous() const { return previous_; } + void set_previous(AssemblerFixup* previous) { previous_ = previous; } + + int position() const { return position_; } + void set_position(int position) { position_ = position; } + + friend class AssemblerBuffer; +}; + + +// Assembler buffers are used to emit binary code. They grow on demand. +class AssemblerBuffer : public ValueObject { + public: + AssemblerBuffer(); + ~AssemblerBuffer(); + + // Basic support for emitting, loading, and storing. + template void Emit(T value) { + ASSERT(HasEnsuredCapacity()); + *reinterpret_cast(cursor_) = value; + cursor_ += sizeof(T); + } + + template T Load(int position) { + ASSERT(position >= 0 && position <= (Size() - static_cast(sizeof(T)))); + return *reinterpret_cast(contents_ + position); + } + + template void Store(int position, T value) { + ASSERT(position >= 0 && position <= (Size() - static_cast(sizeof(T)))); + *reinterpret_cast(contents_ + position) = value; + } + + const ZoneGrowableArray& pointer_offsets() const { +#if defined(DEBUG) + ASSERT(fixups_processed_); +#endif + return *pointer_offsets_; + } + + // Emit an object pointer directly in the code. + void EmitObject(const Object& object); + + // Emit a fixup at the current location. + void EmitFixup(AssemblerFixup* fixup) { + fixup->set_previous(fixup_); + fixup->set_position(Size()); + fixup_ = fixup; + } + + // Get the size of the emitted code. + int Size() const { return cursor_ - contents_; } + uword contents() const { return contents_; } + + // Copy the assembled instructions into the specified memory block + // and apply all fixups. + void FinalizeInstructions(const MemoryRegion& region); + + // To emit an instruction to the assembler buffer, the EnsureCapacity helper + // must be used to guarantee that the underlying data area is big enough to + // hold the emitted instruction. Usage: + // + // AssemblerBuffer buffer; + // AssemblerBuffer::EnsureCapacity ensured(&buffer); + // ... emit bytes for single instruction ... + +#if defined(DEBUG) + class EnsureCapacity : public ValueObject { + public: + explicit EnsureCapacity(AssemblerBuffer* buffer); + ~EnsureCapacity(); + + private: + AssemblerBuffer* buffer_; + int gap_; + + int ComputeGap() { return buffer_->Capacity() - buffer_->Size(); } + }; + + bool has_ensured_capacity_; + bool HasEnsuredCapacity() const { return has_ensured_capacity_; } +#else + class EnsureCapacity : public ValueObject { + public: + explicit EnsureCapacity(AssemblerBuffer* buffer) { + if (buffer->cursor() >= buffer->limit()) buffer->ExtendCapacity(); + } + }; + + // When building the C++ tests, assertion code is enabled. To allow + // asserting that the user of the assembler buffer has ensured the + // capacity needed for emitting, we add a dummy method in non-debug mode. + bool HasEnsuredCapacity() const { return true; } +#endif + + // Returns the position in the instruction stream. + int GetPosition() const { return cursor_ - contents_; } + + private: + // The limit is set to kMinimumGap bytes before the end of the data area. + // This leaves enough space for the longest possible instruction and allows + // for a single, fast space check per instruction. + static const int kMinimumGap = 32; + + uword contents_; + uword cursor_; + uword limit_; + AssemblerFixup* fixup_; + ZoneGrowableArray* pointer_offsets_; +#if defined(DEBUG) + bool fixups_processed_; +#endif + + uword cursor() const { return cursor_; } + uword limit() const { return limit_; } + int Capacity() const { + ASSERT(limit_ >= contents_); + return (limit_ - contents_) + kMinimumGap; + } + + // Process the fixup chain. + void ProcessFixups(const MemoryRegion& region); + + // Compute the limit based on the data area and the capacity. See + // description of kMinimumGap for the reasoning behind the value. + static uword ComputeLimit(uword data, int capacity) { + return data + capacity - kMinimumGap; + } + + void ExtendCapacity(); + + friend class AssemblerFixup; +}; + +} // namespace dart + + +#if defined(TARGET_ARCH_IA32) +#include "vm/assembler_ia32.h" +#elif defined(TARGET_ARCH_X64) +#include "vm/assembler_x64.h" +#elif defined(TARGET_ARCH_ARM) +#include "vm/assembler_arm.h" +#else +#error Unknown architecture. +#endif + +#endif // VM_ASSEMBLER_H_ diff --git a/runtime/vm/assembler_arm.h b/runtime/vm/assembler_arm.h new file mode 100644 index 00000000000..28a1463e524 --- /dev/null +++ b/runtime/vm/assembler_arm.h @@ -0,0 +1,119 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_ASSEMBLER_ARM_H_ +#define VM_ASSEMBLER_ARM_H_ + +#ifndef VM_ASSEMBLER_H_ +#error Do not include assembler_arm.h directly; use assembler.h instead. +#endif + +#include "vm/assert.h" +#include "vm/constants_arm.h" + +namespace dart { + +#if defined(TESTING) || defined(DEBUG) + +#define CHECK_STACK_ALIGNMENT { \ + UNIMPLEMENTED(); \ +} + +#else + +#define CHECK_STACK_ALIGNMENT { } + +#endif + + +class Label : public ValueObject { + public: + Label() : position_(0) { } + + ~Label() { + // Assert if label is being destroyed with unresolved branches pending. + ASSERT(!IsLinked()); + } + + // Returns the position for bound and linked labels. Cannot be used + // for unused labels. + int Position() const { + ASSERT(!IsUnused()); + return IsBound() ? -position_ - kWordSize : position_ - kWordSize; + } + + bool IsBound() const { return position_ < 0; } + bool IsUnused() const { return position_ == 0; } + bool IsLinked() const { return position_ > 0; } + + private: + int position_; + + void Reinitialize() { + position_ = 0; + } + + void BindTo(int position) { + ASSERT(!IsBound()); + position_ = -position - kWordSize; + ASSERT(IsBound()); + } + + void LinkTo(int position) { + ASSERT(!IsBound()); + position_ = position + kWordSize; + ASSERT(IsLinked()); + } + + friend class Assembler; + DISALLOW_COPY_AND_ASSIGN(Label); +}; + + +class Assembler { + public: + Assembler() { } + ~Assembler() { } + + // Macros for High-level operations. + void AddConstant(Register reg, int value, Condition cond = AL) { + UNIMPLEMENTED(); + } + + // Misc. functionality + int CodeSize() const { + UNIMPLEMENTED(); + return 0; + } + int prolog_offset() const { + UNIMPLEMENTED(); + return 0; + } + const ZoneGrowableArray& GetPointerOffsets() const { + UNIMPLEMENTED(); + return *pointer_offsets_; + } + void FinalizeInstructions(const MemoryRegion& region) { + UNIMPLEMENTED(); + } + + // Debugging and bringup support. + void Stop(const char* message) { UNIMPLEMENTED(); } + void Unimplemented(const char* message); + void Untested(const char* message); + void Unreachable(const char* message); + + static void InitializeMemoryWithBreakpoints(uword data, int length) { + UNIMPLEMENTED(); + } + + private: + ZoneGrowableArray* pointer_offsets_; + DISALLOW_ALLOCATION(); + DISALLOW_COPY_AND_ASSIGN(Assembler); +}; + +} // namespace dart + +#endif // VM_ASSEMBLER_ARM_H_ diff --git a/runtime/vm/assembler_ia32.cc b/runtime/vm/assembler_ia32.cc new file mode 100644 index 00000000000..98e57caa1f7 --- /dev/null +++ b/runtime/vm/assembler_ia32.cc @@ -0,0 +1,1463 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" +#if defined(TARGET_ARCH_IA32) + +#include "vm/assembler.h" +#include "vm/heap.h" +#include "vm/memory_region.h" +#include "vm/runtime_entry.h" + +namespace dart { + +class DirectCallRelocation : public AssemblerFixup { + public: + void Process(const MemoryRegion& region, int position) { + // Direct calls are relative to the following instruction on x86. + int32_t pointer = region.Load(position); + int32_t delta = region.start() + position + sizeof(int32_t); + region.Store(position, pointer - delta); + } +}; + + +void Assembler::InitializeMemoryWithBreakpoints(uword data, int length) { + memset(reinterpret_cast(data), Instr::kBreakPointInstruction, length); +} + + +void Assembler::call(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitRegisterOperand(2, reg); +} + + +void Assembler::call(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitOperand(2, address); +} + + +void Assembler::call(Label* label) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xE8); + static const int kSize = 5; + EmitLabel(label, kSize); +} + + +void Assembler::call(const ExternalLabel* label) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + intptr_t call_start = buffer_.GetPosition(); + EmitUint8(0xE8); + EmitFixup(new DirectCallRelocation()); + EmitInt32(label->address()); + ASSERT((buffer_.GetPosition() - call_start) == kCallExternalLabelSize); +} + + +void Assembler::pushl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x50 + reg); +} + + +void Assembler::pushl(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitOperand(6, address); +} + + +void Assembler::pushl(const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x68); + EmitImmediate(imm); +} + + +void Assembler::popl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x58 + reg); +} + + +void Assembler::popl(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x8F); + EmitOperand(0, address); +} + + +void Assembler::movl(Register dst, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xB8 + dst); + EmitImmediate(imm); +} + + +void Assembler::movl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x89); + EmitRegisterOperand(src, dst); +} + + +void Assembler::movl(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x8B); + EmitOperand(dst, src); +} + + +void Assembler::movl(const Address& dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x89); + EmitOperand(src, dst); +} + + +void Assembler::movl(const Address& dst, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC7); + EmitOperand(0, dst); + EmitImmediate(imm); +} + + +void Assembler::movzxb(Register dst, ByteRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xB6); + EmitRegisterOperand(dst, src); +} + + +void Assembler::movzxb(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xB6); + EmitOperand(dst, src); +} + + +void Assembler::movsxb(Register dst, ByteRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xBE); + EmitRegisterOperand(dst, src); +} + + +void Assembler::movsxb(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xBE); + EmitOperand(dst, src); +} + + +void Assembler::movb(Register dst, const Address& src) { + FATAL("Use movzxb or movsxb instead."); +} + + +void Assembler::movb(const Address& dst, ByteRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x88); + EmitOperand(src, dst); +} + + +void Assembler::movb(const Address& dst, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC6); + EmitOperand(EAX, dst); + ASSERT(imm.is_int8()); + EmitUint8(imm.value() & 0xFF); +} + + +void Assembler::movzxw(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xB7); + EmitRegisterOperand(dst, src); +} + + +void Assembler::movzxw(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xB7); + EmitOperand(dst, src); +} + + +void Assembler::movsxw(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xBF); + EmitRegisterOperand(dst, src); +} + + +void Assembler::movsxw(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xBF); + EmitOperand(dst, src); +} + + +void Assembler::movw(Register dst, const Address& src) { + FATAL("Use movzxw or movsxw instead."); +} + + +void Assembler::movw(const Address& dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOperandSizeOverride(); + EmitUint8(0x89); + EmitOperand(src, dst); +} + + +void Assembler::leal(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x8D); + EmitOperand(dst, src); +} + + +void Assembler::cmovs(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x48); + EmitRegisterOperand(dst, src); +} + + +void Assembler::cmovns(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x49); + EmitRegisterOperand(dst, src); +} + + +void Assembler::movss(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x10); + EmitOperand(dst, src); +} + + +void Assembler::movss(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitOperand(src, dst); +} + + +void Assembler::movss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitXmmRegisterOperand(src, dst); +} + + +void Assembler::movd(XmmRegister dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x6E); + EmitOperand(dst, Operand(src)); +} + + +void Assembler::movd(Register dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x7E); + EmitOperand(src, Operand(dst)); +} + + +void Assembler::addss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::addss(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitOperand(dst, src); +} + + +void Assembler::subss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::subss(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitOperand(dst, src); +} + + +void Assembler::mulss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::mulss(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitOperand(dst, src); +} + + +void Assembler::divss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::divss(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitOperand(dst, src); +} + + +void Assembler::flds(const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitOperand(0, src); +} + + +void Assembler::fstps(const Address& dst) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitOperand(3, dst); +} + + +void Assembler::movsd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x10); + EmitOperand(dst, src); +} + + +void Assembler::movsd(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitOperand(src, dst); +} + + +void Assembler::movsd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitXmmRegisterOperand(src, dst); +} + + +void Assembler::addsd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::addsd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitOperand(dst, src); +} + + +void Assembler::subsd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::subsd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitOperand(dst, src); +} + + +void Assembler::mulsd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::mulsd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitOperand(dst, src); +} + + +void Assembler::divsd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::divsd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitOperand(dst, src); +} + + +void Assembler::cvtsi2ss(XmmRegister dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x2A); + EmitOperand(dst, Operand(src)); +} + + +void Assembler::cvtsi2sd(XmmRegister dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x2A); + EmitOperand(dst, Operand(src)); +} + + +void Assembler::cvtss2si(Register dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x2D); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::cvtss2sd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x5A); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::cvtsd2si(Register dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x2D); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::cvttss2si(Register dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x2C); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::cvttsd2si(Register dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x2C); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::cvtsd2ss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x5A); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::cvtdq2pd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0xE6); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::comiss(XmmRegister a, XmmRegister b) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x2F); + EmitXmmRegisterOperand(a, b); +} + + +void Assembler::comisd(XmmRegister a, XmmRegister b) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x2F); + EmitXmmRegisterOperand(a, b); +} + + +void Assembler::sqrtsd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x51); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::sqrtss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x51); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::xorpd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x57); + EmitOperand(dst, src); +} + + +void Assembler::xorpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x57); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::xorps(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x57); + EmitOperand(dst, src); +} + + +void Assembler::xorps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x57); + EmitXmmRegisterOperand(dst, src); +} + + +void Assembler::andpd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x54); + EmitOperand(dst, src); +} + + +void Assembler::fldl(const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xDD); + EmitOperand(0, src); +} + + +void Assembler::fstpl(const Address& dst) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xDD); + EmitOperand(3, dst); +} + + +void Assembler::fnstcw(const Address& dst) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitOperand(7, dst); +} + + +void Assembler::fldcw(const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitOperand(5, src); +} + + +void Assembler::fistpl(const Address& dst) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xDF); + EmitOperand(7, dst); +} + + +void Assembler::fistps(const Address& dst) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xDB); + EmitOperand(3, dst); +} + + +void Assembler::fildl(const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xDF); + EmitOperand(5, src); +} + + +void Assembler::fincstp() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitUint8(0xF7); +} + + +void Assembler::ffree(const Immediate& index) { + ASSERT(index.value() < 7); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xDD); + EmitUint8(0xC0 + index.value()); +} + + +void Assembler::fsin() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitUint8(0xFE); +} + + +void Assembler::fcos() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitUint8(0xFF); +} + + +void Assembler::fptan() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitUint8(0xF2); +} + + +void Assembler::xchgl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x87); + EmitRegisterOperand(dst, src); +} + + +void Assembler::cmpl(Register reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(7, Operand(reg), imm); +} + + +void Assembler::cmpl(Register reg0, Register reg1) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x3B); + EmitOperand(reg0, Operand(reg1)); +} + + +void Assembler::cmpl(Register reg, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x3B); + EmitOperand(reg, address); +} + + +void Assembler::addl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x03); + EmitRegisterOperand(dst, src); +} + + +void Assembler::addl(Register reg, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x03); + EmitOperand(reg, address); +} + + +void Assembler::cmpl(const Address& address, Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x39); + EmitOperand(reg, address); +} + + +void Assembler::cmpl(const Address& address, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(7, address, imm); +} + + +void Assembler::testl(Register reg1, Register reg2) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x85); + EmitRegisterOperand(reg1, reg2); +} + + +void Assembler::testl(Register reg, const Immediate& immediate) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + // For registers that have a byte variant (EAX, EBX, ECX, and EDX) + // we only test the byte register to keep the encoding short. + if (immediate.is_uint8() && reg < 4) { + // Use zero-extended 8-bit immediate. + if (reg == EAX) { + EmitUint8(0xA8); + } else { + EmitUint8(0xF6); + EmitUint8(0xC0 + reg); + } + EmitUint8(immediate.value() & 0xFF); + } else if (reg == EAX) { + // Use short form if the destination is EAX. + EmitUint8(0xA9); + EmitImmediate(immediate); + } else { + EmitUint8(0xF7); + EmitOperand(0, Operand(reg)); + EmitImmediate(immediate); + } +} + + +void Assembler::andl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x23); + EmitOperand(dst, Operand(src)); +} + + +void Assembler::andl(Register dst, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(4, Operand(dst), imm); +} + + +void Assembler::orl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0B); + EmitOperand(dst, Operand(src)); +} + + +void Assembler::orl(Register dst, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(1, Operand(dst), imm); +} + + +void Assembler::xorl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x33); + EmitOperand(dst, Operand(src)); +} + + +void Assembler::addl(Register reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(0, Operand(reg), imm); +} + + +void Assembler::addl(const Address& address, Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x01); + EmitOperand(reg, address); +} + + +void Assembler::addl(const Address& address, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(0, address, imm); +} + + +void Assembler::adcl(Register reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(2, Operand(reg), imm); +} + + +void Assembler::adcl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x13); + EmitOperand(dst, Operand(src)); +} + + +void Assembler::adcl(Register dst, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x13); + EmitOperand(dst, address); +} + + +void Assembler::subl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x2B); + EmitOperand(dst, Operand(src)); +} + + +void Assembler::subl(Register reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(5, Operand(reg), imm); +} + + +void Assembler::subl(Register reg, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x2B); + EmitOperand(reg, address); +} + + +void Assembler::cdq() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x99); +} + + +void Assembler::idivl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitUint8(0xF8 | reg); +} + + +void Assembler::imull(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xAF); + EmitOperand(dst, Operand(src)); +} + + +void Assembler::imull(Register reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x69); + EmitOperand(reg, Operand(reg)); + EmitImmediate(imm); +} + + +void Assembler::imull(Register reg, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xAF); + EmitOperand(reg, address); +} + + +void Assembler::imull(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitOperand(5, Operand(reg)); +} + + +void Assembler::imull(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitOperand(5, address); +} + + +void Assembler::mull(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitOperand(4, Operand(reg)); +} + + +void Assembler::mull(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitOperand(4, address); +} + + +void Assembler::sbbl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x1B); + EmitOperand(dst, Operand(src)); +} + + +void Assembler::sbbl(Register reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(3, Operand(reg), imm); +} + + +void Assembler::sbbl(Register dst, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x1B); + EmitOperand(dst, address); +} + + +void Assembler::incl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x40 + reg); +} + + +void Assembler::incl(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitOperand(0, address); +} + + +void Assembler::decl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x48 + reg); +} + + +void Assembler::decl(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitOperand(1, address); +} + + +void Assembler::shll(Register reg, const Immediate& imm) { + EmitGenericShift(4, reg, imm); +} + + +void Assembler::shll(Register operand, Register shifter) { + EmitGenericShift(4, operand, shifter); +} + + +void Assembler::shrl(Register reg, const Immediate& imm) { + EmitGenericShift(5, reg, imm); +} + + +void Assembler::shrl(Register operand, Register shifter) { + EmitGenericShift(5, operand, shifter); +} + + +void Assembler::sarl(Register reg, const Immediate& imm) { + EmitGenericShift(7, reg, imm); +} + + +void Assembler::sarl(Register operand, Register shifter) { + EmitGenericShift(7, operand, shifter); +} + + +void Assembler::shld(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xA5); + EmitRegisterOperand(src, dst); +} + + +void Assembler::negl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitOperand(3, Operand(reg)); +} + + +void Assembler::notl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitUint8(0xD0 | reg); +} + + +void Assembler::enter(const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC8); + ASSERT(imm.is_uint16()); + EmitUint8(imm.value() & 0xFF); + EmitUint8((imm.value() >> 8) & 0xFF); + EmitUint8(0x00); +} + + +void Assembler::leave() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC9); +} + + +void Assembler::ret() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC3); +} + + +void Assembler::ret(const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC2); + ASSERT(imm.is_uint16()); + EmitUint8(imm.value() & 0xFF); + EmitUint8((imm.value() >> 8) & 0xFF); +} + + + +void Assembler::nop() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x90); +} + + +void Assembler::int3() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xCC); +} + + +void Assembler::hlt() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF4); +} + + +void Assembler::j(Condition condition, Label* label, bool near) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + if (label->IsBound()) { + static const int kShortSize = 2; + static const int kLongSize = 6; + int offset = label->Position() - buffer_.Size(); + ASSERT(offset <= 0); + if (Utils::IsInt(8, offset - kShortSize)) { + EmitUint8(0x70 + condition); + EmitUint8((offset - kShortSize) & 0xFF); + } else { + EmitUint8(0x0F); + EmitUint8(0x80 + condition); + EmitInt32(offset - kLongSize); + } + } else if (near) { + EmitUint8(0x70 + condition); + EmitNearLabelLink(label); + } else { + EmitUint8(0x0F); + EmitUint8(0x80 + condition); + EmitLabelLink(label); + } +} + + +void Assembler::j(Condition condition, const ExternalLabel* label) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x80 + condition); + EmitFixup(new DirectCallRelocation()); + EmitInt32(label->address()); +} + + +void Assembler::jmp(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitRegisterOperand(4, reg); +} + + +void Assembler::jmp(Label* label, bool near) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + if (label->IsBound()) { + static const int kShortSize = 2; + static const int kLongSize = 5; + int offset = label->Position() - buffer_.Size(); + ASSERT(offset <= 0); + if (Utils::IsInt(8, offset - kShortSize)) { + EmitUint8(0xEB); + EmitUint8((offset - kShortSize) & 0xFF); + } else { + EmitUint8(0xE9); + EmitInt32(offset - kLongSize); + } + } else if (near) { + EmitUint8(0xEB); + EmitNearLabelLink(label); + } else { + EmitUint8(0xE9); + EmitLabelLink(label); + } +} + + +void Assembler::jmp(const ExternalLabel* label) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xE9); + EmitFixup(new DirectCallRelocation()); + EmitInt32(label->address()); +} + + +void Assembler::lock() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF0); +} + + +void Assembler::cmpxchgl(const Address& address, Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xB1); + EmitOperand(reg, address); +} + + +void Assembler::AddImmediate(Register reg, const Immediate& imm) { + int value = imm.value(); + if (value > 0) { + if (value == 1) { + incl(reg); + } else if (value != 0) { + addl(reg, imm); + } + } else if (value < 0) { + value = -value; + if (value == 1) { + decl(reg); + } else if (value != 0) { + subl(reg, Immediate(value)); + } + } +} + + +void Assembler::LoadObject(Register dst, const Object& object) { + ASSERT(object.IsZoneHandle()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xB8 + dst); + buffer_.EmitObject(object); +} + + +void Assembler::PushObject(const Object& object) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x68); + buffer_.EmitObject(object); +} + + +void Assembler::CompareObject(Register reg, const Object& object) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + if (reg == EAX) { + EmitUint8(0x05 + (7 << 3)); + buffer_.EmitObject(object); + } else { + EmitUint8(0x81); + EmitOperand(7, Operand(reg)); + buffer_.EmitObject(object); + } +} + + +void Assembler::StoreIntoObject(Register object, + const FieldAddress& dest, + Register value) { + // TODO(iposva): Add write barrier. + movl(dest, value); +} + + +void Assembler::LoadDoubleConstant(XmmRegister dst, double value) { + // TODO(5410843): Need to have a code constants table. + int64_t constant = bit_cast(value); + pushl(Immediate(Utils::High32Bits(constant))); + pushl(Immediate(Utils::Low32Bits(constant))); + movsd(dst, Address(ESP, 0)); + addl(ESP, Immediate(2 * kWordSize)); +} + + +void Assembler::FloatNegate(XmmRegister f) { + static const struct ALIGN16 { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + } float_negate_constant = + { 0x80000000, 0x00000000, 0x80000000, 0x00000000 }; + xorps(f, Address::Absolute(reinterpret_cast(&float_negate_constant))); +} + + +void Assembler::DoubleNegate(XmmRegister d) { + static const struct ALIGN16 { + uint64_t a; + uint64_t b; + } double_negate_constant = + {0x8000000000000000LL, 0x8000000000000000LL}; + xorpd(d, Address::Absolute(reinterpret_cast(&double_negate_constant))); +} + + +void Assembler::DoubleAbs(XmmRegister reg) { + static const struct ALIGN16 { + uint64_t a; + uint64_t b; + } double_abs_constant = + {0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL}; + andpd(reg, Address::Absolute(reinterpret_cast(&double_abs_constant))); +} + + +void Assembler::EnterFrame(intptr_t frame_size) { + if (prolog_offset_ == -1) { + prolog_offset_ = CodeSize(); + } + pushl(EBP); + movl(EBP, ESP); + if (frame_size != 0) { + Immediate frame_space(frame_size); + subl(ESP, frame_space); + } +} + + +void Assembler::LeaveFrame() { + movl(ESP, EBP); + popl(EBP); +} + + +void Assembler::CallRuntimeFromDart(const RuntimeEntry& entry) { + entry.CallFromDart(this); +} + + +void Assembler::CallRuntimeFromStub(const RuntimeEntry& entry) { + entry.CallFromStub(this); +} + + +void Assembler::Align(int alignment, int offset) { + ASSERT(Utils::IsPowerOfTwo(alignment)); + // Emit nop instruction until the real position is aligned. + while (((offset + buffer_.GetPosition()) & (alignment-1)) != 0) { + nop(); + } +} + + +void Assembler::Bind(Label* label) { + int bound = buffer_.Size(); + ASSERT(!label->IsBound()); // Labels can only be bound once. + while (label->IsLinked()) { + int position = label->LinkPosition(); + int next = buffer_.Load(position); + buffer_.Store(position, bound - (position + 4)); + label->position_ = next; + } + while (label->HasNear()) { + int position = label->NearPosition(); + int offset = bound - (position + 1); + ASSERT(Utils::IsInt(8, offset)); + buffer_.Store(position, offset); + } + label->BindTo(bound); +} + + +void Assembler::Stop(const char* message) { + // Emit the message address as immediate operand in the test rax instruction, + // followed by the int3 instruction. + // Execution can be resumed with the 'cont' command in gdb. + testl(EAX, Immediate(reinterpret_cast(message))); + int3(); +} + + +void Assembler::EmitOperand(int rm, const Operand& operand) { + ASSERT(rm >= 0 && rm < 8); + const int length = operand.length_; + ASSERT(length > 0); + // Emit the ModRM byte updated with the given RM value. + ASSERT((operand.encoding_[0] & 0x38) == 0); + EmitUint8(operand.encoding_[0] + (rm << 3)); + // Emit the rest of the encoded operand. + for (int i = 1; i < length; i++) { + EmitUint8(operand.encoding_[i]); + } +} + + +void Assembler::EmitImmediate(const Immediate& imm) { + EmitInt32(imm.value()); +} + + +void Assembler::EmitComplex(int rm, + const Operand& operand, + const Immediate& immediate) { + ASSERT(rm >= 0 && rm < 8); + if (immediate.is_int8()) { + // Use sign-extended 8-bit immediate. + EmitUint8(0x83); + EmitOperand(rm, operand); + EmitUint8(immediate.value() & 0xFF); + } else if (operand.IsRegister(EAX)) { + // Use short form if the destination is eax. + EmitUint8(0x05 + (rm << 3)); + EmitImmediate(immediate); + } else { + EmitUint8(0x81); + EmitOperand(rm, operand); + EmitImmediate(immediate); + } +} + + +void Assembler::EmitLabel(Label* label, int instruction_size) { + if (label->IsBound()) { + int offset = label->Position() - buffer_.Size(); + ASSERT(offset <= 0); + EmitInt32(offset - instruction_size); + } else { + EmitLabelLink(label); + } +} + + +void Assembler::EmitLabelLink(Label* label) { + ASSERT(!label->IsBound()); + int position = buffer_.Size(); + EmitInt32(label->position_); + label->LinkTo(position); +} + + +void Assembler::EmitNearLabelLink(Label* label) { + ASSERT(!label->IsBound()); + int position = buffer_.Size(); + EmitUint8(0); + label->NearLinkTo(position); +} + + +void Assembler::EmitGenericShift(int rm, + Register reg, + const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + ASSERT(imm.is_int8()); + if (imm.value() == 1) { + EmitUint8(0xD1); + EmitOperand(rm, Operand(reg)); + } else { + EmitUint8(0xC1); + EmitOperand(rm, Operand(reg)); + EmitUint8(imm.value() & 0xFF); + } +} + + +void Assembler::EmitGenericShift(int rm, + Register operand, + Register shifter) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + ASSERT(shifter == ECX); + EmitUint8(0xD3); + EmitOperand(rm, Operand(operand)); +} + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/assembler_ia32.h b/runtime/vm/assembler_ia32.h new file mode 100644 index 00000000000..3713a6da978 --- /dev/null +++ b/runtime/vm/assembler_ia32.h @@ -0,0 +1,623 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_ASSEMBLER_IA32_H_ +#define VM_ASSEMBLER_IA32_H_ + +#ifndef VM_ASSEMBLER_H_ +#error Do not include assembler_ia32.h directly; use assembler.h instead. +#endif + +#include "vm/assert.h" +#include "vm/constants_ia32.h" +#include "vm/utils.h" + +namespace dart { + +// Forward declarations. +class RuntimeEntry; + + +#if defined(TESTING) || defined(DEBUG) + +#if defined(TARGET_OS_WINDOWS) +#define CHECK_STACK_ALIGNMENT { \ + uword current_sp; \ + __asm { mov current_sp, esp } \ + ASSERT((OS::ActivationFrameAlignment() == 0) || \ + (Utils::IsAligned(current_sp, OS::ActivationFrameAlignment()))); \ +} +#else +#define CHECK_STACK_ALIGNMENT { \ + uword current_sp; \ + asm volatile("mov %%esp, %[current_sp]" : [current_sp] "=r" (current_sp)); \ + ASSERT((OS::ActivationFrameAlignment() == 0) || \ + (Utils::IsAligned(current_sp, OS::ActivationFrameAlignment()))); \ +} +#endif + +#else + +#define CHECK_STACK_ALIGNMENT { } + +#endif + + +class Immediate : public ValueObject { + public: + explicit Immediate(int32_t value) : value_(value) { } + + int32_t value() const { return value_; } + + bool is_int8() const { return Utils::IsInt(8, value_); } + bool is_uint8() const { return Utils::IsUint(8, value_); } + bool is_uint16() const { return Utils::IsUint(16, value_); } + + private: + const int32_t value_; + + // TODO(5411081): Add DISALLOW_COPY_AND_ASSIGN(Immediate) once the mac + // build issue is resolved. +}; + + +class Operand : public ValueObject { + public: + uint8_t mod() const { + return (encoding_at(0) >> 6) & 3; + } + + Register rm() const { + return static_cast(encoding_at(0) & 7); + } + + ScaleFactor scale() const { + return static_cast((encoding_at(1) >> 6) & 3); + } + + Register index() const { + return static_cast((encoding_at(1) >> 3) & 7); + } + + Register base() const { + return static_cast(encoding_at(1) & 7); + } + + int8_t disp8() const { + ASSERT(length_ >= 2); + return static_cast(encoding_[length_ - 1]); + } + + int32_t disp32() const { + ASSERT(length_ >= 5); + return bit_copy(encoding_[length_ - 4]); + } + + bool IsRegister(Register reg) const { + return ((encoding_[0] & 0xF8) == 0xC0) // Addressing mode is register only. + && ((encoding_[0] & 0x07) == reg); // Register codes match. + } + + protected: + // Operand can be sub classed (e.g: Address). + Operand() : length_(0) { } + + void SetModRM(int mod, Register rm) { + ASSERT((mod & ~3) == 0); + encoding_[0] = (mod << 6) | rm; + length_ = 1; + } + + void SetSIB(ScaleFactor scale, Register index, Register base) { + ASSERT(length_ == 1); + ASSERT((scale & ~3) == 0); + encoding_[1] = (scale << 6) | (index << 3) | base; + length_ = 2; + } + + void SetDisp8(int8_t disp) { + ASSERT(length_ == 1 || length_ == 2); + encoding_[length_++] = static_cast(disp); + } + + void SetDisp32(int32_t disp) { + ASSERT(length_ == 1 || length_ == 2); + int disp_size = sizeof(disp); + memmove(&encoding_[length_], &disp, disp_size); + length_ += disp_size; + } + + private: + uint8_t length_; + uint8_t encoding_[6]; + uint8_t padding_; + + explicit Operand(Register reg) { SetModRM(3, reg); } + + // Get the operand encoding byte at the given index. + uint8_t encoding_at(int index) const { + ASSERT(index >= 0 && index < length_); + return encoding_[index]; + } + + friend class Assembler; + + // TODO(5411081): Add DISALLOW_COPY_AND_ASSIGN(Operand) once the mac + // build issue is resolved. +}; + + +class Address : public Operand { + public: + Address(Register base, int32_t disp) { + if (disp == 0 && base != EBP) { + SetModRM(0, base); + if (base == ESP) SetSIB(TIMES_1, ESP, base); + } else if (Utils::IsInt(8, disp)) { + SetModRM(1, base); + if (base == ESP) SetSIB(TIMES_1, ESP, base); + SetDisp8(disp); + } else { + SetModRM(2, base); + if (base == ESP) SetSIB(TIMES_1, ESP, base); + SetDisp32(disp); + } + } + + Address(Register index, ScaleFactor scale, int32_t disp) { + ASSERT(index != ESP); // Illegal addressing mode. + SetModRM(0, ESP); + SetSIB(scale, index, EBP); + SetDisp32(disp); + } + + Address(Register base, Register index, ScaleFactor scale, int32_t disp) { + ASSERT(index != ESP); // Illegal addressing mode. + if (disp == 0 && base != EBP) { + SetModRM(0, ESP); + SetSIB(scale, index, base); + } else if (Utils::IsInt(8, disp)) { + SetModRM(1, ESP); + SetSIB(scale, index, base); + SetDisp8(disp); + } else { + SetModRM(2, ESP); + SetSIB(scale, index, base); + SetDisp32(disp); + } + } + + static Address Absolute(const uword addr) { + Address result; + result.SetModRM(0, EBP); + result.SetDisp32(addr); + return result; + } + + private: + Address() {} + + // TODO(5411081): Add DISALLOW_COPY_AND_ASSIGN(Address) once the mac + // build issue is resolved. +}; + + +class FieldAddress : public Address { + public: + FieldAddress(Register base, int32_t disp) + : Address(base, disp - kHeapObjectTag) {} + FieldAddress(Register base, Register index, ScaleFactor scale, int32_t disp) + : Address(base, index, scale, disp - kHeapObjectTag) {} +}; + + +class Label : public ValueObject { + public: + Label() : position_(0), unresolved_(0) { +#ifdef DEBUG + for (int i = 0; i < kMaxUnresolvedBranches; i++) { + unresolved_near_positions_[i] = -1; + } +#endif // DEBUG + } + + ~Label() { + // Assert if label is being destroyed with unresolved branches pending. + ASSERT(!IsLinked()); + ASSERT(!HasNear()); + } + + // Returns the position for bound labels. Cannot be used for unused or linked + // labels. + int Position() const { + ASSERT(IsBound()); + return -position_ - kWordSize; + } + + int LinkPosition() const { + ASSERT(IsLinked()); + return position_ - kWordSize; + } + + int NearPosition() { + ASSERT(HasNear()); + return unresolved_near_positions_[--unresolved_]; + } + + bool IsBound() const { return position_ < 0; } + bool IsUnused() const { return (position_ == 0) && (unresolved_ == 0); } + bool IsLinked() const { return position_ > 0; } + bool HasNear() const { return unresolved_ != 0; } + + private: + void BindTo(int position) { + ASSERT(!IsBound()); + ASSERT(!HasNear()); + position_ = -position - kWordSize; + ASSERT(IsBound()); + } + + void LinkTo(int position) { + ASSERT(!IsBound()); + position_ = position + kWordSize; + ASSERT(IsLinked()); + } + + void NearLinkTo(int position) { + ASSERT(!IsBound()); + ASSERT(unresolved_ < kMaxUnresolvedBranches); + unresolved_near_positions_[unresolved_++] = position; + } + + static const int kMaxUnresolvedBranches = 20; + + int position_; + int unresolved_; + int unresolved_near_positions_[kMaxUnresolvedBranches]; + + friend class Assembler; + DISALLOW_COPY_AND_ASSIGN(Label); +}; + + +class Assembler : public ValueObject { + public: + Assembler() : buffer_(), prolog_offset_(-1) { } + ~Assembler() { } + + static const bool kNearJump = true; + static const bool kFarJump = false; + + /* + * Emit Machine Instructions. + */ + void call(Register reg); + void call(const Address& address); + void call(Label* label); + void call(const ExternalLabel* label); + + static const intptr_t kCallExternalLabelSize = 5; + + void pushl(Register reg); + void pushl(const Address& address); + void pushl(const Immediate& imm); + + void popl(Register reg); + void popl(const Address& address); + + void movl(Register dst, const Immediate& src); + void movl(Register dst, Register src); + + void movl(Register dst, const Address& src); + void movl(const Address& dst, Register src); + void movl(const Address& dst, const Immediate& imm); + + void movzxb(Register dst, ByteRegister src); + void movzxb(Register dst, const Address& src); + void movsxb(Register dst, ByteRegister src); + void movsxb(Register dst, const Address& src); + void movb(Register dst, const Address& src); + void movb(const Address& dst, ByteRegister src); + void movb(const Address& dst, const Immediate& imm); + + void movzxw(Register dst, Register src); + void movzxw(Register dst, const Address& src); + void movsxw(Register dst, Register src); + void movsxw(Register dst, const Address& src); + void movw(Register dst, const Address& src); + void movw(const Address& dst, Register src); + + void leal(Register dst, const Address& src); + + void cmovs(Register dst, Register src); + void cmovns(Register dst, Register src); + + void movss(XmmRegister dst, const Address& src); + void movss(const Address& dst, XmmRegister src); + void movss(XmmRegister dst, XmmRegister src); + + void movd(XmmRegister dst, Register src); + void movd(Register dst, XmmRegister src); + + void addss(XmmRegister dst, XmmRegister src); + void addss(XmmRegister dst, const Address& src); + void subss(XmmRegister dst, XmmRegister src); + void subss(XmmRegister dst, const Address& src); + void mulss(XmmRegister dst, XmmRegister src); + void mulss(XmmRegister dst, const Address& src); + void divss(XmmRegister dst, XmmRegister src); + void divss(XmmRegister dst, const Address& src); + + void movsd(XmmRegister dst, const Address& src); + void movsd(const Address& dst, XmmRegister src); + void movsd(XmmRegister dst, XmmRegister src); + + void addsd(XmmRegister dst, XmmRegister src); + void addsd(XmmRegister dst, const Address& src); + void subsd(XmmRegister dst, XmmRegister src); + void subsd(XmmRegister dst, const Address& src); + void mulsd(XmmRegister dst, XmmRegister src); + void mulsd(XmmRegister dst, const Address& src); + void divsd(XmmRegister dst, XmmRegister src); + void divsd(XmmRegister dst, const Address& src); + + void cvtsi2ss(XmmRegister dst, Register src); + void cvtsi2sd(XmmRegister dst, Register src); + + void cvtss2si(Register dst, XmmRegister src); + void cvtss2sd(XmmRegister dst, XmmRegister src); + + void cvtsd2si(Register dst, XmmRegister src); + void cvtsd2ss(XmmRegister dst, XmmRegister src); + + void cvttss2si(Register dst, XmmRegister src); + void cvttsd2si(Register dst, XmmRegister src); + + void cvtdq2pd(XmmRegister dst, XmmRegister src); + + void comiss(XmmRegister a, XmmRegister b); + void comisd(XmmRegister a, XmmRegister b); + + void sqrtsd(XmmRegister dst, XmmRegister src); + void sqrtss(XmmRegister dst, XmmRegister src); + + void xorpd(XmmRegister dst, const Address& src); + void xorpd(XmmRegister dst, XmmRegister src); + void xorps(XmmRegister dst, const Address& src); + void xorps(XmmRegister dst, XmmRegister src); + + void andpd(XmmRegister dst, const Address& src); + + void flds(const Address& src); + void fstps(const Address& dst); + + void fldl(const Address& src); + void fstpl(const Address& dst); + + void fnstcw(const Address& dst); + void fldcw(const Address& src); + + void fistpl(const Address& dst); + void fistps(const Address& dst); + void fildl(const Address& src); + + void fincstp(); + void ffree(const Immediate& index); + + void fsin(); + void fcos(); + void fptan(); + + void xchgl(Register dst, Register src); + + void cmpl(Register reg, const Immediate& imm); + void cmpl(Register reg0, Register reg1); + void cmpl(Register reg, const Address& address); + + void cmpl(const Address& address, Register reg); + void cmpl(const Address& address, const Immediate& imm); + + void testl(Register reg1, Register reg2); + void testl(Register reg, const Immediate& imm); + + void andl(Register dst, const Immediate& imm); + void andl(Register dst, Register src); + + void orl(Register dst, const Immediate& imm); + void orl(Register dst, Register src); + + void xorl(Register dst, Register src); + + void addl(Register dst, Register src); + void addl(Register reg, const Immediate& imm); + void addl(Register reg, const Address& address); + + void addl(const Address& address, Register reg); + void addl(const Address& address, const Immediate& imm); + + void adcl(Register dst, Register src); + void adcl(Register reg, const Immediate& imm); + void adcl(Register dst, const Address& address); + + void subl(Register dst, Register src); + void subl(Register reg, const Immediate& imm); + void subl(Register reg, const Address& address); + + void cdq(); + + void idivl(Register reg); + + void imull(Register dst, Register src); + void imull(Register reg, const Immediate& imm); + void imull(Register reg, const Address& address); + + void imull(Register reg); + void imull(const Address& address); + + void mull(Register reg); + void mull(const Address& address); + + void sbbl(Register dst, Register src); + void sbbl(Register reg, const Immediate& imm); + void sbbl(Register reg, const Address& address); + + void incl(Register reg); + void incl(const Address& address); + + void decl(Register reg); + void decl(const Address& address); + + void shll(Register reg, const Immediate& imm); + void shll(Register operand, Register shifter); + void shrl(Register reg, const Immediate& imm); + void shrl(Register operand, Register shifter); + void sarl(Register reg, const Immediate& imm); + void sarl(Register operand, Register shifter); + void shld(Register dst, Register src); + + void negl(Register reg); + void notl(Register reg); + + void enter(const Immediate& imm); + void leave(); + + void ret(); + void ret(const Immediate& imm); + + void nop(); + void int3(); + void hlt(); + + void j(Condition condition, Label* label, bool near = kFarJump); + void j(Condition condition, const ExternalLabel* label); + + void jmp(Register reg); + void jmp(Label* label, bool near = kFarJump); + void jmp(const ExternalLabel* label); + + void lock(); + void cmpxchgl(const Address& address, Register reg); + + /* + * Macros for High-level operations. + */ + void AddImmediate(Register reg, const Immediate& imm); + + void LoadObject(Register dst, const Object& object); + void PushObject(const Object& object); + void CompareObject(Register reg, const Object& object); + void LoadDoubleConstant(XmmRegister dst, double value); + + void StoreIntoObject(Register object, // Object we are storing into. + const FieldAddress& dest, // Where we are storing into. + Register value); // Value we are storing. + + void DoubleNegate(XmmRegister d); + void FloatNegate(XmmRegister f); + + void DoubleAbs(XmmRegister reg); + + void LockCmpxchgl(const Address& address, Register reg) { + lock(); + cmpxchgl(address, reg); + } + + void EnterFrame(intptr_t frame_space); + void LeaveFrame(); + + void CallRuntimeFromDart(const RuntimeEntry& entry); + void CallRuntimeFromStub(const RuntimeEntry& entry); + + /* + * Misc. functionality + */ + void SmiTag(Register reg) { + addl(reg, reg); + } + + void SmiUntag(Register reg) { + sarl(reg, Immediate(kSmiTagSize)); + } + + int PreferredLoopAlignment() { return 16; } + void Align(int alignment, int offset); + void Bind(Label* label); + + int CodeSize() const { return buffer_.Size(); } + int prolog_offset() const { return prolog_offset_; } + const ZoneGrowableArray& GetPointerOffsets() const { + return buffer_.pointer_offsets(); + } + + void FinalizeInstructions(const MemoryRegion& region) { + buffer_.FinalizeInstructions(region); + } + + // Debugging and bringup support. + void Stop(const char* message); + void Unimplemented(const char* message); + void Untested(const char* message); + void Unreachable(const char* message); + + static void InitializeMemoryWithBreakpoints(uword data, int length); + + private: + AssemblerBuffer buffer_; + int prolog_offset_; + + inline void EmitUint8(uint8_t value); + inline void EmitInt32(int32_t value); + inline void EmitRegisterOperand(int rm, int reg); + inline void EmitXmmRegisterOperand(int rm, XmmRegister reg); + inline void EmitFixup(AssemblerFixup* fixup); + inline void EmitOperandSizeOverride(); + + void EmitOperand(int rm, const Operand& operand); + void EmitImmediate(const Immediate& imm); + void EmitComplex(int rm, const Operand& operand, const Immediate& immediate); + void EmitLabel(Label* label, int instruction_size); + void EmitLabelLink(Label* label); + void EmitNearLabelLink(Label* label); + + void EmitGenericShift(int rm, Register reg, const Immediate& imm); + void EmitGenericShift(int rm, Register operand, Register shifter); + + DISALLOW_ALLOCATION(); + DISALLOW_COPY_AND_ASSIGN(Assembler); +}; + + +inline void Assembler::EmitUint8(uint8_t value) { + buffer_.Emit(value); +} + + +inline void Assembler::EmitInt32(int32_t value) { + buffer_.Emit(value); +} + + +inline void Assembler::EmitRegisterOperand(int rm, int reg) { + ASSERT(rm >= 0 && rm < 8); + buffer_.Emit(0xC0 + (rm << 3) + reg); +} + + +inline void Assembler::EmitXmmRegisterOperand(int rm, XmmRegister reg) { + EmitRegisterOperand(rm, static_cast(reg)); +} + + +inline void Assembler::EmitFixup(AssemblerFixup* fixup) { + buffer_.EmitFixup(fixup); +} + + +inline void Assembler::EmitOperandSizeOverride() { + EmitUint8(0x66); +} + +} // namespace dart + +#endif // VM_ASSEMBLER_IA32_H_ diff --git a/runtime/vm/assembler_ia32_test.cc b/runtime/vm/assembler_ia32_test.cc new file mode 100644 index 00000000000..02c61021c26 --- /dev/null +++ b/runtime/vm/assembler_ia32_test.cc @@ -0,0 +1,1730 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" +#if defined(TARGET_ARCH_IA32) + +#include "vm/assembler.h" +#include "vm/os.h" +#include "vm/unit_test.h" +#include "vm/virtual_memory.h" + +namespace dart { + +#define __ assembler-> + + +ASSEMBLER_TEST_GENERATE(Simple, assembler) { + __ movl(EAX, Immediate(42)); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(Simple, entry) { + typedef int (*SimpleCode)(); + EXPECT_EQ(42, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(ReadArgument, assembler) { + __ movl(EAX, Address(ESP, kWordSize)); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(ReadArgument, entry) { + typedef int (*ReadArgumentCode)(int n); + EXPECT_EQ(42, reinterpret_cast(entry)(42)); + EXPECT_EQ(87, reinterpret_cast(entry)(87)); +} + + +ASSEMBLER_TEST_GENERATE(AddressingModes, assembler) { + __ movl(EAX, Address(ESP, 0)); + __ movl(EAX, Address(EBP, 0)); + __ movl(EAX, Address(EAX, 0)); + + __ movl(EAX, Address(ESP, kWordSize)); + __ movl(EAX, Address(EBP, kWordSize)); + __ movl(EAX, Address(EAX, kWordSize)); + + __ movl(EAX, Address(ESP, -kWordSize)); + __ movl(EAX, Address(EBP, -kWordSize)); + __ movl(EAX, Address(EAX, -kWordSize)); + + __ movl(EAX, Address(ESP, 256 * kWordSize)); + __ movl(EAX, Address(EBP, 256 * kWordSize)); + __ movl(EAX, Address(EAX, 256 * kWordSize)); + + __ movl(EAX, Address(ESP, -256 * kWordSize)); + __ movl(EAX, Address(EBP, -256 * kWordSize)); + __ movl(EAX, Address(EAX, -256 * kWordSize)); + + __ movl(EAX, Address(EAX, TIMES_1)); + __ movl(EAX, Address(EAX, TIMES_2)); + __ movl(EAX, Address(EAX, TIMES_4)); + __ movl(EAX, Address(EAX, TIMES_8)); + + __ movl(EAX, Address(EBP, TIMES_2)); + __ movl(EAX, Address(EAX, TIMES_2)); + + __ movl(EAX, Address(EBP, TIMES_2, kWordSize)); + __ movl(EAX, Address(EAX, TIMES_2, kWordSize)); + + __ movl(EAX, Address(EBP, TIMES_2, 256 * kWordSize)); + __ movl(EAX, Address(EAX, TIMES_2, 256 * kWordSize)); + + __ movl(EAX, Address(EAX, EBP, TIMES_2, 0)); + __ movl(EAX, Address(EAX, EAX, TIMES_2, 0)); + __ movl(EAX, Address(EBP, EBP, TIMES_2, 0)); + __ movl(EAX, Address(EBP, EAX, TIMES_2, 0)); + __ movl(EAX, Address(ESP, EBP, TIMES_2, 0)); + __ movl(EAX, Address(ESP, EAX, TIMES_2, 0)); + + __ movl(EAX, Address(EAX, EBP, TIMES_2, kWordSize)); + __ movl(EAX, Address(EAX, EAX, TIMES_2, kWordSize)); + __ movl(EAX, Address(EBP, EBP, TIMES_2, kWordSize)); + __ movl(EAX, Address(EBP, EAX, TIMES_2, kWordSize)); + __ movl(EAX, Address(ESP, EBP, TIMES_2, kWordSize)); + __ movl(EAX, Address(ESP, EAX, TIMES_2, kWordSize)); + + __ movl(EAX, Address(EAX, EBP, TIMES_2, 256 * kWordSize)); + __ movl(EAX, Address(EAX, EAX, TIMES_2, 256 * kWordSize)); + __ movl(EAX, Address(EBP, EBP, TIMES_2, 256 * kWordSize)); + __ movl(EAX, Address(EBP, EAX, TIMES_2, 256 * kWordSize)); + __ movl(EAX, Address(ESP, EBP, TIMES_2, 256 * kWordSize)); + __ movl(EAX, Address(ESP, EAX, TIMES_2, 256 * kWordSize)); +} + + +ASSEMBLER_TEST_RUN(AddressingModes, entry) { + // Avoid running the code since it is constructed to lead to crashes. +} + + +ASSEMBLER_TEST_GENERATE(JumpAroundCrash, assembler) { + Label done; + // Make sure all the condition jumps work. + for (Condition condition = OVERFLOW; + condition <= GREATER; + condition = static_cast(condition + 1)) { + __ j(condition, &done); + } + // This isn't strictly necessary, but we do an unconditional + // jump around the crashing code anyway. + __ jmp(&done); + + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + + __ Bind(&done); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(JumpAroundCrash, entry) { + Instr* instr = Instr::At(entry); + EXPECT(!instr->IsBreakPoint()); + typedef void (*JumpAroundCrashCode)(); + reinterpret_cast(entry)(); +} + + +ASSEMBLER_TEST_GENERATE(NearJumpAroundCrash, assembler) { + Label done; + // Make sure all the condition jumps work. + for (Condition condition = OVERFLOW; + condition <= GREATER; + condition = static_cast(condition + 1)) { + __ j(condition, &done, Assembler::kNearJump); + } + // This isn't strictly necessary, but we do an unconditional + // jump around the crashing code anyway. + __ jmp(&done, Assembler::kNearJump); + + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + + __ Bind(&done); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(NearJumpAroundCrash, entry) { + typedef void (*NearJumpAroundCrashCode)(); + reinterpret_cast(entry)(); +} + + +ASSEMBLER_TEST_GENERATE(SimpleLoop, assembler) { + __ movl(EAX, Immediate(0)); + __ movl(ECX, Immediate(0)); + Label loop; + __ Bind(&loop); + __ addl(EAX, Immediate(2)); + __ incl(ECX); + __ cmpl(ECX, Immediate(87)); + __ j(LESS, &loop); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(SimpleLoop, entry) { + typedef int (*SimpleLoopCode)(); + EXPECT_EQ(2 * 87, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(Increment, assembler) { + __ movl(EAX, Immediate(0)); + __ pushl(EAX); + __ incl(Address(ESP, 0)); + __ movl(ECX, Address(ESP, 0)); + __ incl(ECX); + __ popl(EAX); + __ movl(EAX, ECX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(Increment, entry) { + typedef int (*IncrementCode)(); + EXPECT_EQ(2, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(Decrement, assembler) { + __ movl(EAX, Immediate(2)); + __ pushl(EAX); + __ decl(Address(ESP, 0)); + __ movl(ECX, Address(ESP, 0)); + __ decl(ECX); + __ popl(EAX); + __ movl(EAX, ECX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(Decrement, entry) { + typedef int (*DecrementCode)(); + EXPECT_EQ(0, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(AddressBinOp, assembler) { + __ movl(EAX, Address(ESP, kWordSize)); + __ addl(EAX, Address(ESP, kWordSize)); + __ incl(EAX); + __ subl(EAX, Address(ESP, kWordSize)); + __ imull(EAX, Address(ESP, kWordSize)); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(AddressBinOp, entry) { + typedef int (*AddressBinOpCode)(int a); + EXPECT_EQ((2 + 2 + 1 - 2) * 2, reinterpret_cast(entry)(2)); +} + + +ASSEMBLER_TEST_GENERATE(SignedMultiply, assembler) { + __ movl(EAX, Immediate(2)); + __ movl(ECX, Immediate(4)); + __ imull(EAX, ECX); + __ imull(EAX, Immediate(1000)); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(SignedMultiply, entry) { + typedef int (*SignedMultiply)(); + EXPECT_EQ(8000, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(OverflowSignedMultiply, assembler) { + __ movl(EDX, Immediate(0)); + __ movl(EAX, Immediate(0x0fffffff)); + __ movl(ECX, Immediate(0x0fffffff)); + __ imull(EAX, ECX); + __ imull(EAX, EDX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(OverflowSignedMultiply, entry) { + typedef int (*OverflowSignedMultiply)(); + EXPECT_EQ(0, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(SignedMultiply1, assembler) { + __ pushl(EBX); // preserve EBX. + __ movl(EBX, Immediate(2)); + __ movl(ECX, Immediate(4)); + __ imull(EBX, ECX); + __ imull(EBX, Immediate(1000)); + __ movl(EAX, EBX); + __ popl(EBX); // restore EBX. + __ ret(); +} + + +ASSEMBLER_TEST_RUN(SignedMultiply1, entry) { + typedef int (*SignedMultiply1)(); + EXPECT_EQ(8000, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(Negate, assembler) { + __ movl(ECX, Immediate(42)); + __ negl(ECX); + __ movl(EAX, ECX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(Negate, entry) { + typedef int (*Negate)(); + EXPECT_EQ(-42, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(MoveExtend, assembler) { + __ pushl(EBX); // preserve EBX. + __ movl(EDX, Immediate(0x1234ffff)); + __ movzxb(EAX, DL); // EAX = 0xff + __ movsxw(EBX, EDX); // EBX = -1 + __ movzxw(ECX, EDX); // ECX = 0xffff + __ addl(EBX, ECX); + __ addl(EAX, EBX); + __ popl(EBX); // restore EBX. + __ ret(); +} + + +ASSEMBLER_TEST_RUN(MoveExtend, entry) { + typedef int (*MoveExtend)(); + EXPECT_EQ(0xff - 1 + 0xffff, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(MoveExtendMemory, assembler) { + __ pushl(EBX); // preserve EBX. + __ movl(EDX, Immediate(0x1234ffff)); + + __ pushl(EDX); + __ movzxb(EAX, Address(ESP, 0)); // EAX = 0xff + __ movsxw(EBX, Address(ESP, 0)); // EBX = -1 + __ movzxw(ECX, Address(ESP, 0)); // ECX = 0xffff + __ addl(ESP, Immediate(kWordSize)); + + __ addl(EBX, ECX); + __ addl(EAX, EBX); + __ popl(EBX); // restore EBX. + __ ret(); +} + + +ASSEMBLER_TEST_RUN(MoveExtendMemory, entry) { + typedef int (*MoveExtendMemory)(); + EXPECT_EQ(0xff - 1 + 0xffff, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(Bitwise, assembler) { + __ movl(ECX, Immediate(42)); + __ xorl(ECX, ECX); + __ orl(ECX, Immediate(256)); + __ movl(EAX, Immediate(4)); + __ orl(ECX, EAX); + __ movl(EAX, Immediate(0xfff0)); + __ andl(ECX, EAX); + __ movl(EAX, Immediate(1)); + __ orl(ECX, EAX); + __ movl(EAX, ECX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(Bitwise, entry) { + typedef int (*Bitwise)(); + EXPECT_EQ(256 + 1, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(LogicalOps, assembler) { + Label donetest1; + __ movl(EAX, Immediate(4)); + __ andl(EAX, Immediate(2)); + __ cmpl(EAX, Immediate(0)); + __ j(EQUAL, &donetest1); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest1); + + Label donetest2; + __ movl(ECX, Immediate(4)); + __ andl(ECX, Immediate(4)); + __ cmpl(ECX, Immediate(0)); + __ j(NOT_EQUAL, &donetest2); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest2); + + Label donetest3; + __ movl(EAX, Immediate(0)); + __ orl(EAX, Immediate(0)); + __ cmpl(EAX, Immediate(0)); + __ j(EQUAL, &donetest3); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest3); + + Label donetest4; + __ movl(EAX, Immediate(4)); + __ orl(EAX, Immediate(0)); + __ cmpl(EAX, Immediate(0)); + __ j(NOT_EQUAL, &donetest4); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest4); + + Label donetest5; + __ movl(EAX, Immediate(1)); + __ shll(EAX, Immediate(1)); + __ cmpl(EAX, Immediate(2)); + __ j(EQUAL, &donetest5); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest5); + + Label donetest6; + __ movl(EAX, Immediate(1)); + __ shll(EAX, Immediate(3)); + __ cmpl(EAX, Immediate(8)); + __ j(EQUAL, &donetest6); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest6); + + Label donetest7; + __ movl(EAX, Immediate(2)); + __ shrl(EAX, Immediate(1)); + __ cmpl(EAX, Immediate(1)); + __ j(EQUAL, &donetest7); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest7); + + Label donetest8; + __ movl(EAX, Immediate(8)); + __ shrl(EAX, Immediate(3)); + __ cmpl(EAX, Immediate(1)); + __ j(EQUAL, &donetest8); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest8); + + Label donetest9; + __ movl(EAX, Immediate(1)); + __ movl(ECX, Immediate(3)); + __ shll(EAX, ECX); + __ cmpl(EAX, Immediate(8)); + __ j(EQUAL, &donetest9); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest9); + + Label donetest10; + __ movl(EAX, Immediate(8)); + __ movl(ECX, Immediate(3)); + __ shrl(EAX, ECX); + __ cmpl(EAX, Immediate(1)); + __ j(EQUAL, &donetest10); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest10); + + Label donetest11; + __ movl(EAX, Immediate(1)); + __ shll(EAX, Immediate(31)); + __ shrl(EAX, Immediate(3)); + __ cmpl(EAX, Immediate(0x10000000)); + __ j(EQUAL, &donetest11); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest11); + + Label donetest12; + __ movl(EAX, Immediate(1)); + __ shll(EAX, Immediate(31)); + __ sarl(EAX, Immediate(3)); + __ cmpl(EAX, Immediate(0xf0000000)); + __ j(EQUAL, &donetest12); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest12); + + Label donetest13; + __ movl(EAX, Immediate(1)); + __ movl(ECX, Immediate(3)); + __ shll(EAX, Immediate(31)); + __ sarl(EAX, ECX); + __ cmpl(EAX, Immediate(0xf0000000)); + __ j(EQUAL, &donetest13); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest13); + + __ movl(EAX, Immediate(0)); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(LogicalOps, entry) { + typedef int (*LogicalOpsCode)(); + EXPECT_EQ(0, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(LogicalTest, assembler) { + __ pushl(EBX); // save EBX. + Label donetest1; + __ movl(EAX, Immediate(4)); + __ movl(ECX, Immediate(2)); + __ testl(EAX, ECX); + __ j(EQUAL, &donetest1); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest1); + + Label donetest2; + __ movl(EDX, Immediate(4)); + __ movl(ECX, Immediate(4)); + __ testl(EDX, ECX); + __ j(NOT_EQUAL, &donetest2); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest2); + + Label donetest3; + __ movl(EAX, Immediate(0)); + __ testl(EAX, Immediate(0)); + __ j(EQUAL, &donetest3); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest3); + + Label donetest4; + __ movl(EBX, Immediate(4)); + __ testl(EBX, Immediate(4)); + __ j(NOT_EQUAL, &donetest4); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest4); + + Label donetest5; + __ movl(EBX, Immediate(0xff)); + __ testl(EBX, Immediate(0xff)); + __ j(NOT_EQUAL, &donetest5); + // Be sure to skip this crashing code. + __ movl(EAX, Immediate(0)); + __ movl(Address(EAX, 0), EAX); + __ Bind(&donetest5); + + __ movl(EAX, Immediate(0)); + __ popl(EBX); // restore EBX. + __ ret(); +} + + +ASSEMBLER_TEST_RUN(LogicalTest, entry) { + typedef int (*LogicalTestCode)(); + EXPECT_EQ(0, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(CompareSwapEQ, assembler) { + __ movl(EAX, Immediate(0)); + __ pushl(EAX); + __ movl(EAX, Immediate(4)); + __ movl(ECX, Immediate(0)); + __ movl(Address(ESP, 0), EAX); + __ LockCmpxchgl(Address(ESP, 0), ECX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(CompareSwapEQ, entry) { + typedef int (*CompareSwapEQCode)(); + EXPECT_EQ(0, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(CompareSwapNEQ, assembler) { + __ movl(EAX, Immediate(0)); + __ pushl(EAX); + __ movl(EAX, Immediate(2)); + __ movl(ECX, Immediate(4)); + __ movl(Address(ESP, 0), ECX); + __ LockCmpxchgl(Address(ESP, 0), ECX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(CompareSwapNEQ, entry) { + typedef int (*CompareSwapNEQCode)(); + EXPECT_EQ(4, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(SignedDivide, assembler) { + __ movl(EAX, Immediate(-87)); + __ movl(EDX, Immediate(123)); + __ cdq(); + __ movl(ECX, Immediate(42)); + __ idivl(ECX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(SignedDivide, entry) { + typedef int (*SignedDivide)(); + EXPECT_EQ(-87 / 42, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(Exchange, assembler) { + __ movl(EAX, Immediate(123456789)); + __ movl(EDX, Immediate(987654321)); + __ xchgl(EAX, EDX); + __ subl(EAX, EDX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(Exchange, entry) { + typedef int (*Exchange)(); + EXPECT_EQ(987654321 - 123456789, reinterpret_cast(entry)()); +} + + +static int ComputeStackSpaceReservation(int needed, int fixed) { + static const int kFrameAlignment = OS::ActivationFrameAlignment(); + return (kFrameAlignment > 0) + ? Utils::RoundUp(needed + fixed, kFrameAlignment) - fixed + : needed; +} + + +static int LeafReturn42() { + return 42; +} + + +static int LeafReturnArgument(int x) { + return x + 87; +} + + +ASSEMBLER_TEST_GENERATE(CallSimpleLeaf, assembler) { + ExternalLabel call1("LeafReturn42", reinterpret_cast(LeafReturn42)); + ExternalLabel call2("LeafReturnArgument", + reinterpret_cast(LeafReturnArgument)); + int space = ComputeStackSpaceReservation(0, 4); + __ AddImmediate(ESP, Immediate(-space)); + __ call(&call1); + __ AddImmediate(ESP, Immediate(space)); + space = ComputeStackSpaceReservation(4, 4); + __ AddImmediate(ESP, Immediate(-space)); + __ movl(Address(ESP, 0), EAX); + __ call(&call2); + __ AddImmediate(ESP, Immediate(space)); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(CallSimpleLeaf, entry) { + typedef int (*CallSimpleLeafCode)(); + EXPECT_EQ(42 + 87, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(JumpSimpleLeaf, assembler) { + ExternalLabel call1("LeafReturn42", reinterpret_cast(LeafReturn42)); + Label L; + int space = ComputeStackSpaceReservation(0, 4); + __ AddImmediate(ESP, Immediate(-space)); + __ call(&L); + __ AddImmediate(ESP, Immediate(space)); + __ ret(); + __ Bind(&L); + __ jmp(&call1); +} + + +ASSEMBLER_TEST_RUN(JumpSimpleLeaf, entry) { + typedef int (*JumpSimpleLeafCode)(); + EXPECT_EQ(42, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(JumpConditionalSimpleLeaf, assembler) { + ExternalLabel call1("LeafReturn42", reinterpret_cast(LeafReturn42)); + Label L; + int space = ComputeStackSpaceReservation(0, 4); + __ AddImmediate(ESP, Immediate(-space)); + __ call(&L); + __ AddImmediate(ESP, Immediate(space)); + __ ret(); + __ Bind(&L); + __ cmpl(EAX, EAX); + __ j(EQUAL, &call1); + __ int3(); +} + + +ASSEMBLER_TEST_RUN(JumpConditionalSimpleLeaf, entry) { + typedef int (*JumpConditionalSimpleLeafCode)(); + EXPECT_EQ(42, reinterpret_cast(entry)()); +} + + +ASSEMBLER_TEST_GENERATE(SingleFPMoves, assembler) { + __ movl(EAX, Immediate(bit_cast(234.0f))); + __ movd(XMM0, EAX); + __ movss(XMM1, XMM0); + __ movss(XMM2, XMM1); + __ movss(XMM3, XMM2); + __ movss(XMM4, XMM3); + __ movss(XMM5, XMM4); + __ movss(XMM6, XMM5); + __ movss(XMM7, XMM6); + __ pushl(EAX); + __ movl(Address(ESP, 0), Immediate(0)); + __ movss(Address(ESP, 0), XMM7); + __ flds(Address(ESP, 0)); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(SingleFPMoves, entry) { + typedef float (*SingleFPMovesCode)(); + float res = reinterpret_cast(entry)(); + EXPECT_EQ(234.0f, res); +} + + + +ASSEMBLER_TEST_GENERATE(SingleFPMoves2, assembler) { + __ pushl(EBX); // preserve EBX. + __ pushl(ECX); // preserve ECX. + __ movl(EBX, Immediate(bit_cast(234.0f))); + __ movd(XMM0, EBX); + __ movss(XMM1, XMM0); + __ movd(ECX, XMM1); + __ pushl(ECX); + __ flds(Address(ESP, 0)); + __ popl(EAX); + __ popl(ECX); + __ popl(EBX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(SingleFPMoves2, entry) { + typedef float (*SingleFPMoves2Code)(); + float res = reinterpret_cast(entry)(); + EXPECT_EQ(234.0f, res); +} + + +ASSEMBLER_TEST_GENERATE(SingleFPUStackMoves, assembler) { + __ movl(EAX, Immediate(1131020288)); // 234.0f + __ pushl(EAX); + __ flds(Address(ESP, 0)); + __ xorl(ECX, ECX); + __ pushl(ECX); + __ fstps(Address(ESP, 0)); + __ popl(EAX); + __ popl(ECX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(SingleFPUStackMoves, entry) { + typedef int (*SingleFPUStackMovesCode)(); + int res = reinterpret_cast(entry)(); + EXPECT_EQ(234.0f, (bit_cast(res))); +} + + +ASSEMBLER_TEST_GENERATE(SingleFPOperations, assembler) { + __ movl(EAX, Immediate(bit_cast(12.3f))); + __ movd(XMM0, EAX); + __ movl(EAX, Immediate(bit_cast(3.4f))); + __ movd(XMM1, EAX); + __ addss(XMM0, XMM1); // 15.7f + __ mulss(XMM0, XMM1); // 53.38f + __ subss(XMM0, XMM1); // 49.98f + __ divss(XMM0, XMM1); // 14.7f + __ pushl(EAX); + __ movss(Address(ESP, 0), XMM0); + __ flds(Address(ESP, 0)); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(SingleFPOperations, entry) { + typedef float (*SingleFPOperationsCode)(); + float res = reinterpret_cast(entry)(); + EXPECT_FLOAT_EQ(14.7f, res, 0.001f); +} + + +ASSEMBLER_TEST_GENERATE(SingleFPOperationsStack, assembler) { + __ movl(EAX, Immediate(bit_cast(12.3f))); + __ movd(XMM0, EAX); + __ addss(XMM0, Address(ESP, kWordSize)); // 15.7f + __ mulss(XMM0, Address(ESP, kWordSize)); // 53.38f + __ subss(XMM0, Address(ESP, kWordSize)); // 49.98f + __ divss(XMM0, Address(ESP, kWordSize)); // 14.7f + __ pushl(EAX); + __ movss(Address(ESP, 0), XMM0); + __ flds(Address(ESP, 0)); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(SingleFPOperationsStack, entry) { + typedef float (*SingleFPOperationsStackCode)(float f); + float res = reinterpret_cast(entry)(3.4); + EXPECT_FLOAT_EQ(14.7f, res, 0.001f); +} + + +ASSEMBLER_TEST_GENERATE(DoubleFPMoves, assembler) { + int64_t l = bit_cast(1024.67); + __ movl(EAX, Immediate(Utils::High32Bits(l))); + __ pushl(EAX); + __ movl(EAX, Immediate(Utils::Low32Bits(l))); + __ pushl(EAX); + __ movsd(XMM0, Address(ESP, 0)); + __ movsd(XMM1, XMM0); + __ movsd(XMM2, XMM1); + __ movsd(XMM3, XMM2); + __ movsd(XMM4, XMM3); + __ movsd(XMM5, XMM4); + __ movsd(XMM6, XMM5); + __ movsd(XMM7, XMM6); + __ movl(Address(ESP, 0), Immediate(0)); + __ movl(Address(ESP, kWordSize), Immediate(0)); + __ movsd(Address(ESP, 0), XMM7); + __ fldl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(DoubleFPMoves, entry) { + typedef double (*DoubleFPMovesCode)(); + double res = reinterpret_cast(entry)(); + EXPECT_FLOAT_EQ(1024.67, res, 0.0001); +} + +ASSEMBLER_TEST_GENERATE(DoubleFPUStackMoves, assembler) { + int64_t l = bit_cast(1024.67); + __ movl(EAX, Immediate(Utils::High32Bits(l))); + __ pushl(EAX); + __ movl(EAX, Immediate(Utils::Low32Bits(l))); + __ pushl(EAX); + __ fldl(Address(ESP, 0)); + __ movl(Address(ESP, 0), Immediate(0)); + __ movl(Address(ESP, kWordSize), Immediate(0)); + __ fstpl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EDX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(DoubleFPUStackMoves, entry) { + typedef int64_t (*DoubleFPUStackMovesCode)(); + int64_t res = reinterpret_cast(entry)(); + EXPECT_FLOAT_EQ(1024.67, (bit_cast(res)), 0.001); +} + + +ASSEMBLER_TEST_GENERATE(DoubleFPOperations, assembler) { + int64_t l = bit_cast(12.3); + __ movl(EAX, Immediate(Utils::High32Bits(l))); + __ pushl(EAX); + __ movl(EAX, Immediate(Utils::Low32Bits(l))); + __ pushl(EAX); + __ movsd(XMM0, Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + l = bit_cast(3.4); + __ movl(EAX, Immediate(Utils::High32Bits(l))); + __ pushl(EAX); + __ movl(EAX, Immediate(Utils::Low32Bits(l))); + __ pushl(EAX); + __ movsd(XMM1, Address(ESP, 0)); + __ addsd(XMM0, XMM1); // 15.7 + __ mulsd(XMM0, XMM1); // 53.38 + __ subsd(XMM0, XMM1); // 49.98 + __ divsd(XMM0, XMM1); // 14.7 + __ movsd(Address(ESP, 0), XMM0); + __ fldl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(DoubleFPOperations, entry) { + typedef double (*DoubleFPOperationsCode)(); + double res = reinterpret_cast(entry)(); + EXPECT_FLOAT_EQ(14.7, res, 0.001); +} + + +ASSEMBLER_TEST_GENERATE(DoubleFPOperationsStack, assembler) { + int64_t l = bit_cast(12.3); + __ movl(EAX, Immediate(Utils::High32Bits(l))); + __ pushl(EAX); + __ movl(EAX, Immediate(Utils::Low32Bits(l))); + __ pushl(EAX); + __ movsd(XMM0, Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + + __ addsd(XMM0, Address(ESP, kWordSize)); // 15.7 + __ mulsd(XMM0, Address(ESP, kWordSize)); // 53.38 + __ subsd(XMM0, Address(ESP, kWordSize)); // 49.98 + __ divsd(XMM0, Address(ESP, kWordSize)); // 14.7 + + __ pushl(EAX); + __ pushl(EAX); + __ movsd(Address(ESP, 0), XMM0); + __ fldl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(DoubleFPOperationsStack, entry) { + typedef double (*DoubleFPOperationsStackCode)(double d); + double res = reinterpret_cast(entry)(3.4); + EXPECT_FLOAT_EQ(14.7, res, 0.001); +} + + +ASSEMBLER_TEST_GENERATE(IntToDoubleConversion, assembler) { + __ movl(EDX, Immediate(6)); + __ cvtsi2sd(XMM1, EDX); + __ pushl(EAX); + __ pushl(EAX); + __ movsd(Address(ESP, 0), XMM1); + __ fldl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(IntToDoubleConversion, entry) { + typedef double (*IntToDoubleConversionCode)(); + double res = reinterpret_cast(entry)(); + EXPECT_FLOAT_EQ(6.0, res, 0.001); +} + + +ASSEMBLER_TEST_GENERATE(IntToFloatConversion, assembler) { + __ movl(EDX, Immediate(6)); + __ cvtsi2ss(XMM1, EDX); + __ pushl(EAX); + __ movss(Address(ESP, 0), XMM1); + __ flds(Address(ESP, 0)); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(IntToFloatConversion, entry) { + typedef float (*IntToFloatConversionCode)(); + float res = reinterpret_cast(entry)(); + EXPECT_FLOAT_EQ(6.0, res, 0.001); +} + + +ASSEMBLER_TEST_GENERATE(FloatToIntConversionRound, assembler) { + __ movsd(XMM1, Address(ESP, kWordSize)); + __ cvtss2si(EDX, XMM1); + __ movl(EAX, EDX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(FloatToIntConversionRound, entry) { + typedef int (*FloatToIntConversionRoundCode)(float f); + int res = reinterpret_cast(entry)(12.3); + EXPECT_EQ(12, res); + res = reinterpret_cast(entry)(12.8); + EXPECT_EQ(13, res); +} + + +ASSEMBLER_TEST_GENERATE(FloatToIntConversionTrunc, assembler) { + __ movsd(XMM1, Address(ESP, kWordSize)); + __ cvttss2si(EDX, XMM1); + __ movl(EAX, EDX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(FloatToIntConversionTrunc, entry) { + typedef int (*FloatToIntConversionTruncCode)(float f); + int res = reinterpret_cast(entry)(12.3); + EXPECT_EQ(12, res); + res = reinterpret_cast(entry)(12.8); + EXPECT_EQ(12, res); +} + + +ASSEMBLER_TEST_GENERATE(FloatToDoubleConversion, assembler) { + __ movl(EAX, Immediate(bit_cast(12.3f))); + __ movd(XMM1, EAX); + __ xorl(EAX, EAX); + __ cvtss2sd(XMM2, XMM1); + __ pushl(EAX); + __ pushl(EAX); + __ movsd(Address(ESP, 0), XMM2); + __ fldl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(FloatToDoubleConversion, entry) { + typedef double (*FloatToDoubleConversionCode)(); + double res = reinterpret_cast(entry)(); + EXPECT_FLOAT_EQ(12.3, res, 0.001); +} + + +ASSEMBLER_TEST_GENERATE(FloatCompare, assembler) { + // Count errors in EAX. EAX is zero if no errors found. + Label is_nan, is_above, is_ok, cont_1, cont_2; + // Test 12.3f vs 12.5f. + __ xorl(EAX, EAX); + __ movl(EDX, Immediate(bit_cast(12.3f))); + __ movd(XMM0, EDX); + __ movl(EDX, Immediate(bit_cast(12.5f))); + __ movd(XMM1, EDX); + __ comiss(XMM0, XMM1); + __ j(PARITY_EVEN, &is_nan); + __ Bind(&cont_1); + __ j(ABOVE, &is_above); + __ Bind(&cont_2); + __ j(BELOW, &is_ok); + __ incl(EAX); + __ Bind(&is_ok); + + // Test NaN. + Label is_nan_ok; + // Create NaN by dividing 0.0f/0.0f. + __ movl(EDX, Immediate(bit_cast(0.0f))); + __ movd(XMM1, EDX); + __ divss(XMM1, XMM1); + __ comiss(XMM1, XMM1); + __ j(PARITY_EVEN, &is_nan_ok); + __ incl(EAX); + __ Bind(&is_nan_ok); + + // EAX is 0 if all tests passed. + __ ret(); + + __ Bind(&is_nan); + __ incl(EAX); + __ jmp(&cont_1); + + __ Bind(&is_above); + __ incl(EAX); + __ jmp(&cont_2); +} + + +ASSEMBLER_TEST_RUN(FloatCompare, entry) { + typedef int (*FloatCompareCode)(); + int res = reinterpret_cast(entry)(); + EXPECT_EQ(0, res); +} + + +ASSEMBLER_TEST_GENERATE(DoubleCompare, assembler) { + int64_t a = bit_cast(12.3); + int64_t b = bit_cast(12.5); + + __ movl(EDX, Immediate(Utils::High32Bits(a))); + __ pushl(EDX); + __ movl(EDX, Immediate(Utils::Low32Bits(a))); + __ pushl(EDX); + __ movsd(XMM0, Address(ESP, 0)); + __ popl(EDX); + __ popl(EDX); + + __ movl(EDX, Immediate(Utils::High32Bits(b))); + __ pushl(EDX); + __ movl(EDX, Immediate(Utils::Low32Bits(b))); + __ pushl(EDX); + __ movsd(XMM1, Address(ESP, 0)); + __ popl(EDX); + __ popl(EDX); + + // Count errors in EAX. EAX is zero if no errors found. + Label is_nan, is_above, is_ok, cont_1, cont_2; + // Test 12.3 vs 12.5. + __ xorl(EAX, EAX); + __ comisd(XMM0, XMM1); + __ j(PARITY_EVEN, &is_nan); + __ Bind(&cont_1); + __ j(ABOVE, &is_above); + __ Bind(&cont_2); + __ j(BELOW, &is_ok); + __ incl(EAX); + __ Bind(&is_ok); + + // Test NaN. + Label is_nan_ok; + // Create NaN by dividing 0.0d/0.0d. + int64_t zero = bit_cast(0.0); + __ movl(EDX, Immediate(Utils::High32Bits(zero))); + __ pushl(EDX); + __ movl(EDX, Immediate(Utils::Low32Bits(zero))); + __ pushl(EDX); + __ movsd(XMM1, Address(ESP, 0)); + __ popl(EDX); + __ popl(EDX); + + __ divsd(XMM1, XMM1); + __ comisd(XMM1, XMM1); + __ j(PARITY_EVEN, &is_nan_ok); + __ incl(EAX); + __ Bind(&is_nan_ok); + + // EAX is 0 if all tests passed. + __ ret(); + + __ Bind(&is_nan); + __ incl(EAX); + __ jmp(&cont_1); + + __ Bind(&is_above); + __ incl(EAX); + __ jmp(&cont_2); +} + + +ASSEMBLER_TEST_RUN(DoubleCompare, entry) { + typedef int (*DoubleCompareCode)(); + int res = reinterpret_cast(entry)(); + EXPECT_EQ(0, res); +} + + +ASSEMBLER_TEST_GENERATE(DoubleToFloatConversion, assembler) { + int64_t l = bit_cast(12.3); + __ movl(EAX, Immediate(Utils::High32Bits(l))); + __ pushl(EAX); + __ movl(EAX, Immediate(Utils::Low32Bits(l))); + __ pushl(EAX); + __ movsd(XMM0, Address(ESP, 0)); + __ cvtsd2ss(XMM1, XMM0); + __ movss(Address(ESP, 0), XMM1); + __ flds(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(DoubleToFloatConversion, entry) { + typedef float (*DoubleToFloatConversionCode)(); + float res = reinterpret_cast(entry)(); + EXPECT_FLOAT_EQ(12.3f, res, 0.001); +} + + +ASSEMBLER_TEST_GENERATE(DoubleToIntConversionRound, assembler) { + __ movsd(XMM3, Address(ESP, kWordSize)); + __ cvtsd2si(EAX, XMM3); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(DoubleToIntConversionRound, entry) { + typedef int (*DoubleToIntConversionRoundCode)(double d); + int res = reinterpret_cast(entry)(12.3); + EXPECT_EQ(12, res); + res = reinterpret_cast(entry)(12.8); + EXPECT_EQ(13, res); +} + + +ASSEMBLER_TEST_GENERATE(DoubleToIntConversionTrunc, assembler) { + __ movsd(XMM3, Address(ESP, kWordSize)); + __ cvttsd2si(EAX, XMM3); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(DoubleToIntConversionTrunc, entry) { + typedef int (*DoubleToIntConversionTruncCode)(double d); + int res = reinterpret_cast(entry)(12.3); + EXPECT_EQ(12, res); + res = reinterpret_cast(entry)(12.8); + EXPECT_EQ(12, res); +} + + +static const double kDoubleConst = 3.226; + +ASSEMBLER_TEST_GENERATE(GlobalAddress, assembler) { + __ movsd(XMM0, Address::Absolute(reinterpret_cast(&kDoubleConst))); + __ pushl(EAX); + __ pushl(EAX); + __ movsd(Address(ESP, 0), XMM0); + __ fldl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(GlobalAddress, entry) { + typedef double (*GlobalAddressCode)(); + double res = reinterpret_cast(entry)(); + EXPECT_FLOAT_EQ(kDoubleConst, res, 0.000001); +} + + +ASSEMBLER_TEST_GENERATE(Sine, assembler) { + __ flds(Address(ESP, kWordSize)); + __ fsin(); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(Sine, entry) { + typedef float (*SineCode)(float f); + const float kFloatConst = 0.7; + float res = reinterpret_cast(entry)(kFloatConst); + EXPECT_FLOAT_EQ(sin(kFloatConst), res, 0.0001); +} + + +ASSEMBLER_TEST_GENERATE(Cosine, assembler) { + __ flds(Address(ESP, kWordSize)); + __ fcos(); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(Cosine, entry) { + typedef float (*CosineCode)(float f); + const float kFloatConst = 0.7; + float res = reinterpret_cast(entry)(kFloatConst); + EXPECT_FLOAT_EQ(cos(kFloatConst), res, 0.0001); +} + + +ASSEMBLER_TEST_GENERATE(Tangent, assembler) { + __ fldl(Address(ESP, kWordSize)); + __ fptan(); + __ ffree(Immediate(0)); + __ fincstp(); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(Tangent, entry) { + typedef double (*TangentCode)(double d); + const double kDoubleConst = 0.6108652375000001; + double res = reinterpret_cast(entry)(kDoubleConst); + EXPECT_FLOAT_EQ(tan(kDoubleConst), res, 0.0001); +} + + +ASSEMBLER_TEST_GENERATE(SquareRootFloat, assembler) { + __ movss(XMM0, Address(ESP, kWordSize)); + __ sqrtss(XMM1, XMM0); + __ pushl(EAX); + __ movss(Address(ESP, 0), XMM1); + __ flds(Address(ESP, 0)); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(SquareRootFloat, entry) { + typedef float (*SquareRootFloatCode)(float f); + const float kFloatConst = 0.7; + float res = reinterpret_cast(entry)(kFloatConst); + EXPECT_FLOAT_EQ(sqrt(kFloatConst), res, 0.0001); +} + + +ASSEMBLER_TEST_GENERATE(SquareRootDouble, assembler) { + __ movsd(XMM0, Address(ESP, kWordSize)); + __ sqrtsd(XMM1, XMM0); + __ pushl(EAX); + __ pushl(EAX); + __ movsd(Address(ESP, 0), XMM1); + __ fldl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(SquareRootDouble, entry) { + typedef double (*SquareRootDoubleCode)(double d); + const double kDoubleConst = .7; + double res = reinterpret_cast(entry)(kDoubleConst); + EXPECT_FLOAT_EQ(sqrt(kDoubleConst), res, 0.0001); +} + + +ASSEMBLER_TEST_GENERATE(FloatNegate, assembler) { + __ movss(XMM0, Address(ESP, kWordSize)); + __ FloatNegate(XMM0); + __ pushl(EAX); + __ movss(Address(ESP, 0), XMM0); + __ flds(Address(ESP, 0)); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(FloatNegate, entry) { + typedef float (*FloatNegateCode)(float f); + const float kFloatConst = 12.345; + float res = reinterpret_cast(entry)(kFloatConst); + EXPECT_FLOAT_EQ(-kFloatConst, res, 0.0001); +} + + +ASSEMBLER_TEST_GENERATE(DoubleNegate, assembler) { + __ movsd(XMM0, Address(ESP, kWordSize)); + __ DoubleNegate(XMM0); + __ pushl(EAX); + __ pushl(EAX); + __ movsd(Address(ESP, 0), XMM0); + __ fldl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(DoubleNegate, entry) { + typedef double (*DoubleNegateCode)(double f); + const double kDoubleConst = 12.345; + double res = reinterpret_cast(entry)(kDoubleConst); + EXPECT_FLOAT_EQ(-kDoubleConst, res, 0.0001); +} + + +ASSEMBLER_TEST_GENERATE(LongMulReg, assembler) { + __ movl(ECX, Address(ESP, kWordSize)); + __ movl(EAX, Address(ESP, 2 * kWordSize)); + __ imull(ECX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(LongMulReg, entry) { + typedef int64_t (*LongMulRegCode)(int a, int b); + const int a = -12; + const int b = 13; + const int64_t mul_res = a * b; + int64_t res = reinterpret_cast(entry)(a, b); + EXPECT_EQ(mul_res, res); +} + + +ASSEMBLER_TEST_GENERATE(LongMulAddress, assembler) { + __ movl(EAX, Address(ESP, 2 * kWordSize)); + __ imull(Address(ESP, kWordSize)); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(LongMulAddress, entry) { + typedef int64_t (*LongMulAddressCode)(int a, int b); + const int a = -12; + const int b = 13; + const int64_t mul_res = a * b; + int64_t res = reinterpret_cast(entry)(a, b); + EXPECT_EQ(mul_res, res); +} + + +ASSEMBLER_TEST_GENERATE(LongUnsignedMulReg, assembler) { + __ movl(ECX, Address(ESP, kWordSize)); + __ movl(EAX, Address(ESP, 2 * kWordSize)); + __ mull(ECX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(LongUnsignedMulReg, entry) { + typedef uint64_t (*LongUnsignedMulRegCode)(uint32_t a, uint32_t b); + uint32_t a = 3; + uint32_t b = 13; + uint64_t mul_res = a * b; + uint64_t res = reinterpret_cast(entry)(a, b); + EXPECT_EQ(mul_res, res); + a = 4021288948u; + b = 13; + res = reinterpret_cast(entry)(a, b); + mul_res = static_cast(a) * static_cast(b); + EXPECT_EQ(mul_res, res); +} + + +ASSEMBLER_TEST_GENERATE(LongUnsignedMulAddress, assembler) { + __ movl(EAX, Address(ESP, 2 * kWordSize)); + __ mull(Address(ESP, kWordSize)); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(LongUnsignedMulAddress, entry) { + typedef uint64_t (*LongUnsignedMulAddressCode)(uint32_t a, uint32_t b); + uint32_t a = 12; + uint32_t b = 13; + uint64_t mul_res = a * b; + uint64_t res = reinterpret_cast(entry)(a, b); + EXPECT_EQ(mul_res, res); + a = 4294967284u; + b = 13; + res = reinterpret_cast(entry)(a, b); + mul_res = static_cast(a) * static_cast(b); + EXPECT_EQ(mul_res, res); +} + + +ASSEMBLER_TEST_GENERATE(LongAddReg, assembler) { + // Preserve clobbered callee-saved register (EBX). + __ pushl(EBX); + __ movl(EAX, Address(ESP, 2 * kWordSize)); // left low. + __ movl(EDX, Address(ESP, 3 * kWordSize)); // left high. + __ movl(ECX, Address(ESP, 4 * kWordSize)); // right low. + __ movl(EBX, Address(ESP, 5 * kWordSize)); // right high + __ addl(EAX, ECX); + __ adcl(EDX, EBX); + __ popl(EBX); + // Result is in EAX/EDX. + __ ret(); +} + + +ASSEMBLER_TEST_RUN(LongAddReg, entry) { + typedef int64_t (*LongAddRegCode)(int64_t a, int64_t b); + int64_t a = 12; + int64_t b = 14; + int64_t res = reinterpret_cast(entry)(a, b); + EXPECT_EQ((a + b), res); + a = 2147483647; + b = 600000; + res = reinterpret_cast(entry)(a, b); + EXPECT_EQ((a + b), res); +} + + +ASSEMBLER_TEST_GENERATE(LongAddAddress, assembler) { + // Preserve clobbered callee-saved register (EBX). + __ movl(EAX, Address(ESP, 1 * kWordSize)); // left low. + __ movl(EDX, Address(ESP, 2 * kWordSize)); // left high. + __ addl(EAX, Address(ESP, 3 * kWordSize)); // low. + __ adcl(EDX, Address(ESP, 4 * kWordSize)); // high. + // Result is in EAX/EDX. + __ ret(); +} + + +ASSEMBLER_TEST_RUN(LongAddAddress, entry) { + typedef int64_t (*LongAddAddressCode)(int64_t a, int64_t b); + int64_t a = 12; + int64_t b = 14; + int64_t res = reinterpret_cast(entry)(a, b); + EXPECT_EQ((a + b), res); + a = 2147483647; + b = 600000; + res = reinterpret_cast(entry)(a, b); + EXPECT_EQ((a + b), res); +} + + +ASSEMBLER_TEST_GENERATE(LongSubReg, assembler) { + // Preserve clobbered callee-saved register (EBX). + __ pushl(EBX); + __ movl(EAX, Address(ESP, 2 * kWordSize)); // left low. + __ movl(EDX, Address(ESP, 3 * kWordSize)); // left high. + __ movl(ECX, Address(ESP, 4 * kWordSize)); // right low. + __ movl(EBX, Address(ESP, 5 * kWordSize)); // right high + __ subl(EAX, ECX); + __ sbbl(EDX, EBX); + __ popl(EBX); + // Result is in EAX/EDX. + __ ret(); +} + + +ASSEMBLER_TEST_RUN(LongSubReg, entry) { + typedef int64_t (*LongSubRegCode)(int64_t a, int64_t b); + int64_t a = 12; + int64_t b = 14; + int64_t res = reinterpret_cast(entry)(a, b); + EXPECT_EQ((a - b), res); + a = 600000; + b = 2147483647; + res = reinterpret_cast(entry)(a, b); + EXPECT_EQ((a - b), res); +} + + +ASSEMBLER_TEST_GENERATE(LongSubAddress, assembler) { + // Preserve clobbered callee-saved register (EBX). + __ movl(EAX, Address(ESP, 1 * kWordSize)); // left low. + __ movl(EDX, Address(ESP, 2 * kWordSize)); // left high. + __ subl(EAX, Address(ESP, 3 * kWordSize)); // low. + __ sbbl(EDX, Address(ESP, 4 * kWordSize)); // high. + // Result is in EAX/EDX. + __ ret(); +} + + +ASSEMBLER_TEST_RUN(LongSubAddress, entry) { + typedef int64_t (*LongSubAddressCode)(int64_t a, int64_t b); + int64_t a = 12; + int64_t b = 14; + int64_t res = reinterpret_cast(entry)(a, b); + EXPECT_EQ((a - b), res); + a = 600000; + b = 2147483647; + res = reinterpret_cast(entry)(a, b); + EXPECT_EQ((a - b), res); +} + + +// Testing only the lower 64-bit value of 'cvtdq2pd'. +ASSEMBLER_TEST_GENERATE(IntegerToDoubleConversion, assembler) { + __ movsd(XMM1, Address(ESP, kWordSize)); + __ cvtdq2pd(XMM2, XMM1); + __ pushl(EAX); + __ pushl(EAX); + __ movsd(Address(ESP, 0), XMM2); + __ fldl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(IntegerToDoubleConversion, entry) { + typedef double (*IntegerToDoubleConversionCode)(int32_t); + const int32_t val = -12; + double res = reinterpret_cast(entry)(val); + EXPECT_FLOAT_EQ(static_cast(val), res, 0.001); +} + + +// Implement with truncation. +ASSEMBLER_TEST_GENERATE(FPUStoreLong, assembler) { + __ fldl(Address(ESP, kWordSize)); + __ pushl(EAX); + __ pushl(EAX); + __ fnstcw(Address(ESP, 0)); + __ movzxw(EAX, Address(ESP, 0)); + __ orl(EAX, Immediate(0x0c00)); + __ movw(Address(ESP, kWordSize), EAX); + __ fldcw(Address(ESP, kWordSize)); + __ pushl(EAX); + __ pushl(EAX); + __ fistpl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EDX); + __ fldcw(Address(ESP, 0)); + __ addl(ESP, Immediate(kWordSize * 2)); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(FPUStoreLong, entry) { + typedef int64_t (*FPUStoreLongCode)(double d); + double val = 12.2; + int64_t res = reinterpret_cast(entry)(val); + EXPECT_EQ(static_cast(val), res); + val = -12.2; + res = reinterpret_cast(entry)(val); + EXPECT_EQ(static_cast(val), res); + val = 12.8; + res = reinterpret_cast(entry)(val); + EXPECT_EQ(static_cast(val), res); + val = -12.8; + res = reinterpret_cast(entry)(val); + EXPECT_EQ(static_cast(val), res); +} + + +ASSEMBLER_TEST_GENERATE(XorpdZeroing, assembler) { + __ movsd(XMM0, Address(ESP, kWordSize)); + __ xorpd(XMM0, XMM0); + __ pushl(EAX); + __ pushl(EAX); + __ movsd(Address(ESP, 0), XMM0); + __ fldl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(XorpdZeroing, entry) { + typedef double (*XorpdZeroingCode)(double d); + double res = reinterpret_cast(entry)(12.56e3); + EXPECT_FLOAT_EQ(0.0, res, 0.0001); +} + + +ASSEMBLER_TEST_GENERATE(DoubleAbs, assembler) { + __ movsd(XMM0, Address(ESP, kWordSize)); + __ DoubleAbs(XMM0); + __ pushl(EAX); + __ pushl(EAX); + __ movsd(Address(ESP, 0), XMM0); + __ fldl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(DoubleAbs, entry) { + typedef double (*DoubleAbsCode)(double d); + double val = -12.45; + double res = reinterpret_cast(entry)(val); + EXPECT_FLOAT_EQ(-val, res, 0.001); + val = 12.45; + res = reinterpret_cast(entry)(val); + EXPECT_FLOAT_EQ(val, res, 0.001); +} + + +// Return -1 if signed, 1 if not signed and 0 otherwise. +ASSEMBLER_TEST_GENERATE(ConditionalMovesSign, assembler) { + // Preserve clobbered callee-saved register (EBX). + __ pushl(EBX); + + __ movl(EDX, Address(ESP, 2 * kWordSize)); + __ xorl(EAX, EAX); + __ movl(EBX, Immediate(1)); + __ movl(ECX, Immediate(-1)); + __ testl(EDX, EDX); + __ cmovs(EAX, ECX); // return -1. + __ testl(EDX, EDX); + __ cmovns(EAX, EBX); // return 1. + + // Restore callee-saved register (EBX) and return. + __ popl(EBX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(ConditionalMovesSign, entry) { + typedef int (*ConditionalMovesSignCode)(int i); + int res = reinterpret_cast(entry)(785); + EXPECT_EQ(1, res); + res = reinterpret_cast(entry)(-12); + EXPECT_EQ(-1, res); +} + + +ASSEMBLER_TEST_GENERATE(TestLoadDoubleConstant, assembler) { + __ LoadDoubleConstant(XMM3, -12.34); + __ pushl(EAX); + __ pushl(EAX); + __ movsd(Address(ESP, 0), XMM3); + __ fldl(Address(ESP, 0)); + __ popl(EAX); + __ popl(EAX); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(TestLoadDoubleConstant, entry) { + typedef double (*TestLoadDoubleConstantCode)(); + double res = reinterpret_cast(entry)(); + EXPECT_FLOAT_EQ(-12.34, res, 0.0001); +} + + +ASSEMBLER_TEST_GENERATE(TestObjectCompare, assembler) { + ObjectStore* object_store = Isolate::Current()->object_store(); + const Object& obj = Object::ZoneHandle(object_store->smi_class()); + Label fail; + __ LoadObject(EAX, obj); + __ CompareObject(EAX, obj); + __ j(NOT_EQUAL, &fail); + __ LoadObject(ECX, obj); + __ CompareObject(ECX, obj); + __ j(NOT_EQUAL, &fail); + __ movl(EAX, Immediate(1)); // OK + __ ret(); + __ Bind(&fail); + __ movl(EAX, Immediate(0)); // Fail. + __ ret(); +} + + +ASSEMBLER_TEST_RUN(TestObjectCompare, entry) { + typedef bool (*TestObjectCompare)(); + bool res = reinterpret_cast(entry)(); + EXPECT_EQ(true, res); +} + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/assembler_macros.h b/runtime/vm/assembler_macros.h new file mode 100644 index 00000000000..878de4e7fe0 --- /dev/null +++ b/runtime/vm/assembler_macros.h @@ -0,0 +1,18 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_ASSEMBLER_MACROS_H_ +#define VM_ASSEMBLER_MACROS_H_ + +#if defined(TARGET_ARCH_IA32) +#include "vm/assembler_macros_ia32.h" +#elif defined(TARGET_ARCH_X64) +// Not yet implemented. +#elif defined(TARGET_ARCH_ARM) +// Not yet implemented. +#else +#error Unknown architecture. +#endif + +#endif // VM_ASSEMBLER_MACROS_H_ diff --git a/runtime/vm/assembler_macros_ia32.cc b/runtime/vm/assembler_macros_ia32.cc new file mode 100644 index 00000000000..b35f1475735 --- /dev/null +++ b/runtime/vm/assembler_macros_ia32.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" +#if defined(TARGET_ARCH_IA32) + +#include "vm/assembler_macros.h" + +#include "vm/assembler.h" + +namespace dart { + +DECLARE_FLAG(bool, inline_alloc); + +#define __ assembler-> + +// Static. +void AssemblerMacros::TryAllocate(Assembler* assembler, + const Class& cls, + Register class_reg, + Label* failure, + Register instance_reg) { +#if defined(DEBUG) + Label ok; + __ LoadObject(instance_reg, cls); + __ cmpl(instance_reg, class_reg); + __ j(EQUAL, &ok, Assembler::kNearJump); + __ Stop("AssemblerMacros::TryAllocate, wrong arguments"); + __ Bind(&ok); +#endif + ASSERT(failure != NULL); + ASSERT(class_reg != instance_reg); + if (FLAG_inline_alloc) { + Heap* heap = Isolate::Current()->heap(); + const intptr_t instance_size = cls.instance_size(); + __ movl(instance_reg, Address::Absolute(heap->TopAddress())); + __ addl(instance_reg, Immediate(instance_size)); + // instance_reg: potential next object start. + __ cmpl(instance_reg, Address::Absolute(heap->EndAddress())); + __ j(ABOVE_EQUAL, failure, Assembler::kNearJump); + // Successfully allocated the object, now update top to point to + // next object start and store the class in the class field of object. + __ movl(Address::Absolute(heap->TopAddress()), instance_reg); + ASSERT(instance_size >= kHeapObjectTag); + __ subl(instance_reg, Immediate(instance_size - kHeapObjectTag)); + __ movl(FieldAddress(instance_reg, Instance::class_offset()), class_reg); + } else { + __ jmp(failure); + } +} + +#undef __ + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/assembler_macros_ia32.h b/runtime/vm/assembler_macros_ia32.h new file mode 100644 index 00000000000..b18190bf824 --- /dev/null +++ b/runtime/vm/assembler_macros_ia32.h @@ -0,0 +1,40 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// The class 'AssemblerMacros' contains assembler instruction groups that +// are used in Dart. + +#ifndef VM_ASSEMBLER_MACROS_IA32_H_ +#define VM_ASSEMBLER_MACROS_IA32_H_ + +#ifndef VM_ASSEMBLER_MACROS_H_ +#error Do not include assembler_macros_ia32.h directly; use assembler_macros.h. +#endif + +#include "vm/allocation.h" +#include "vm/constants_ia32.h" + +namespace dart { + +// Forward declarations. +class Assembler; +class Class; +class Label; + +class AssemblerMacros : public AllStatic { + public: + // Inlined allocation of an instance of class 'cls', code has no runtime + // calls. Jump to 'failure' if the instance cannot be allocated here. + // Class must be loaded in 'class_reg'. Allocated instance is returned + // in 'instance_reg'. Only the class field of the object is initialized. + // 'class_reg' and 'instance_reg' may not be the same register. + static void TryAllocate(Assembler* assembler, + const Class& cls, + Register class_reg, + Label* failure, + Register instance_reg); +}; + +} // namespace dart. + +#endif // VM_ASSEMBLER_MACROS_IA32_H_ diff --git a/runtime/vm/assembler_x64.h b/runtime/vm/assembler_x64.h new file mode 100644 index 00000000000..dc29e8bb20c --- /dev/null +++ b/runtime/vm/assembler_x64.h @@ -0,0 +1,119 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_ASSEMBLER_X64_H_ +#define VM_ASSEMBLER_X64_H_ + +#ifndef VM_ASSEMBLER_H_ +#error Do not include assembler_x64.h directly; use assembler.h instead. +#endif + +#include "vm/assert.h" +#include "vm/constants_x64.h" + +namespace dart { + +#if defined(TESTING) || defined(DEBUG) + +#define CHECK_STACK_ALIGNMENT { \ + UNIMPLEMENTED(); \ +} + +#else + +#define CHECK_STACK_ALIGNMENT { } + +#endif + + +class Label : public ValueObject { + public: + Label() : position_(0) { } + + ~Label() { + // Assert if label is being destroyed with unresolved branches pending. + ASSERT(!IsLinked()); + } + + // Returns the position for bound and linked labels. Cannot be used + // for unused labels. + int Position() const { + ASSERT(!IsUnused()); + return IsBound() ? -position_ - kWordSize : position_ - kWordSize; + } + + bool IsBound() const { return position_ < 0; } + bool IsUnused() const { return position_ == 0; } + bool IsLinked() const { return position_ > 0; } + + private: + int position_; + + void Reinitialize() { + position_ = 0; + } + + void BindTo(int position) { + ASSERT(!IsBound()); + position_ = -position - kWordSize; + ASSERT(IsBound()); + } + + void LinkTo(int position) { + ASSERT(!IsBound()); + position_ = position + kWordSize; + ASSERT(IsLinked()); + } + + friend class Assembler; + DISALLOW_COPY_AND_ASSIGN(Label); +}; + + +class Assembler { + public: + Assembler() { } + ~Assembler() { } + + // Macros for High-level operations. + void AddConstant(Register reg, int value) { + UNIMPLEMENTED(); + } + + // Misc. functionality + int CodeSize() const { + UNIMPLEMENTED(); + return 0; + } + int prolog_offset() const { + UNIMPLEMENTED(); + return 0; + } + const ZoneGrowableArray& GetPointerOffsets() const { + UNIMPLEMENTED(); + return *pointer_offsets_; + } + void FinalizeInstructions(const MemoryRegion& region) { + UNIMPLEMENTED(); + } + + // Debugging and bringup support. + void Stop(const char* message) { UNIMPLEMENTED(); } + void Unimplemented(const char* message); + void Untested(const char* message); + void Unreachable(const char* message); + + static void InitializeMemoryWithBreakpoints(uword data, int length) { + UNIMPLEMENTED(); + } + + private: + ZoneGrowableArray* pointer_offsets_; + DISALLOW_ALLOCATION(); + DISALLOW_COPY_AND_ASSIGN(Assembler); +}; + +} // namespace dart + +#endif // VM_ASSEMBLER_X64_H_ diff --git a/runtime/vm/assert.cc b/runtime/vm/assert.cc new file mode 100644 index 00000000000..ecda4d12b8f --- /dev/null +++ b/runtime/vm/assert.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include + +#include "vm/assert.h" + +namespace dart { + +// Exit with a failure code when we miss an EXPECT check. +static void failed_exit(void) { + exit(255); +} + +void DynamicAssertionHelper::Fail(const char* format, ...) { + std::stringstream stream; + stream << file_ << ":" << line_ << ": error: "; + + va_list arguments; + va_start(arguments, format); + char buffer[KB]; + vsnprintf(buffer, sizeof(buffer), format, arguments); + va_end(arguments); + stream << buffer << std::endl; + + // Get the message from the string stream and dump it on stderr. + std::string message = stream.str(); + fprintf(stderr, "%s", message.c_str()); + + // In case of failed assertions, abort right away. Otherwise, wait + // until the program is exiting before producing a non-zero exit + // code through abort. + // TODO(5411324): replace std::abort with OS::Abort so that we can handle + // restoring of signal handlers before aborting. + if (kind_ == ASSERT) { + std::abort(); + } + static bool failed = false; + if (!failed) { + std::atexit(&failed_exit); + } + failed = true; +} + +} // namespace dart diff --git a/runtime/vm/assert.h b/runtime/vm/assert.h new file mode 100644 index 00000000000..59ec911ac33 --- /dev/null +++ b/runtime/vm/assert.h @@ -0,0 +1,261 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_ASSERT_H_ +#define VM_ASSERT_H_ + +// TODO(5411406): include sstream for now, once we have a Utils::toString() +// implemented for all the primitive types we can replace the usage of +// sstream by Utils::toString() +#if defined(TESTING) +#include +#include +#endif + +#include "vm/globals.h" + +#if !defined(DEBUG) && !defined(NDEBUG) +#error neither DEBUG nor NDEBUG defined +#elif defined(DEBUG) && defined(NDEBUG) +#error both DEBUG and NDEBUG defined +#endif + +namespace dart { + +class DynamicAssertionHelper { + public: + enum Kind { + ASSERT, + EXPECT + }; + + DynamicAssertionHelper(const char* file, int line, Kind kind) + : file_(file), line_(line), kind_(kind) { } + + void Fail(const char* format, ...); + +#if defined(TESTING) + template + void Equals(const E& expected, const A& actual); + + template + void FloatEquals(const E& expected, const A& actual, const T& tol); + + template + void StringEquals(const E& expected, const A& actual); + + template + void LessThan(const E& left, const A& right); + + template + void LessEqual(const E& left, const A& right); + + template + void GreaterThan(const E& left, const A& right); + + template + void GreaterEqual(const E& left, const A& right); +#endif + + private: + const char* const file_; + const int line_; + const Kind kind_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicAssertionHelper); +}; + + +class Assert: public DynamicAssertionHelper { + public: + Assert(const char* file, int line) + : DynamicAssertionHelper(file, line, ASSERT) { } +}; + + +class Expect: public DynamicAssertionHelper { + public: + Expect(const char* file, int line) + : DynamicAssertionHelper(file, line, EXPECT) { } +}; + + +#if defined(TESTING) +// Only allow the expensive (with respect to code size) assertions +// in testing code. +template +void DynamicAssertionHelper::Equals(const E& expected, const A& actual) { + if (actual == expected) return; + std::stringstream ess, ass; + ess << expected; + ass << actual; + std::string es = ess.str(), as = ass.str(); + Fail("expected: <%s> but was: <%s>", es.c_str(), as.c_str()); +} + + +template +void DynamicAssertionHelper::FloatEquals(const E& expected, + const A& actual, + const T& tol) { + if (((expected - tol) <= actual) && (actual <= (expected + tol))) { + return; + } + std::stringstream ess, ass, tolss; + ess << expected; + ass << actual; + tolss << tol; + std::string es = ess.str(), as = ass.str(), tols = tolss.str(); + Fail("expected: <%s> but was: <%s> (tolerance: <%s>)", + es.c_str(), + as.c_str(), + tols.c_str()); +} + + +template +void DynamicAssertionHelper::StringEquals(const E& expected, const A& actual) { + std::stringstream ess, ass; + ess << expected; + ass << actual; + std::string es = ess.str(), as = ass.str(); + if (as == es) return; + Fail("expected: <\"%s\"> but was: <\"%s\">", es.c_str(), as.c_str()); +} + + +template +void DynamicAssertionHelper::LessThan(const E& left, const A& right) { + if (left < right) return; + std::stringstream ess, ass; + ess << left; + ass << right; + std::string es = ess.str(), as = ass.str(); + Fail("expected: %s < %s", es.c_str(), as.c_str()); +} + + +template +void DynamicAssertionHelper::LessEqual(const E& left, const A& right) { + if (left <= right) return; + std::stringstream ess, ass; + ess << left; + ass << right; + std::string es = ess.str(), as = ass.str(); + Fail("expected: %s <= %s", es.c_str(), as.c_str()); +} + + +template +void DynamicAssertionHelper::GreaterThan(const E& left, const A& right) { + if (left > right) return; + std::stringstream ess, ass; + ess << left; + ass << right; + std::string es = ess.str(), as = ass.str(); + Fail("expected: %s > %s", es.c_str(), as.c_str()); +} + + +template +void DynamicAssertionHelper::GreaterEqual(const E& left, const A& right) { + if (left >= right) return; + std::stringstream ess, ass; + ess << left; + ass << right; + std::string es = ess.str(), as = ass.str(); + Fail("expected: %s >= %s", es.c_str(), as.c_str()); +} +#endif + +} // namespace dart + + +#define FATAL(error) \ + dart::Assert(__FILE__, __LINE__).Fail("%s", error) + +#define FATAL1(format, p1) \ + dart::Assert(__FILE__, __LINE__).Fail(format, (p1)) + +#define FATAL2(format, p1, p2) \ + dart::Assert(__FILE__, __LINE__).Fail(format, (p1), (p2)) + +#define UNIMPLEMENTED() \ + FATAL("unimplemented code") + +#define UNREACHABLE() \ + FATAL("unreachable code") + + +#if defined(DEBUG) +// DEBUG binaries use assertions in the code. +// Note: We wrap the if statement in a do-while so that we get a compile +// error if there is no semicolon after ASSERT(condition). This +// ensures that we get the same behavior on DEBUG and RELEASE builds. + +#define ASSERT(cond) \ + do { \ + if (!(cond)) dart::Assert(__FILE__, __LINE__).Fail("expected: %s", #cond); \ + } while (false) + +// DEBUG_ASSERT allows identifiers in condition to be undeclared in release +// mode. +#define DEBUG_ASSERT(cond) \ + if (!(cond)) dart::Assert(__FILE__, __LINE__).Fail("expected: %s", #cond); + +#else // if defined(DEBUG) + +// In order to avoid variable unused warnings for code that only uses +// a variable in an ASSERT or EXPECT, we make sure to use the macro +// argument. +#define ASSERT(condition) do {} while (false && (condition)) + +#define DEBUG_ASSERT(cond) + +#endif // if defined(DEBUG) + + +#if defined(TESTING) +#define EXPECT(condition) \ + if (!(condition)) { \ + dart::Expect(__FILE__, __LINE__).Fail("expected: %s", #condition); \ + } + +#define EXPECT_EQ(expected, actual) \ + dart::Expect(__FILE__, __LINE__).Equals((expected), (actual)) + +#define EXPECT_FLOAT_EQ(expected, actual, tol) \ + dart::Expect(__FILE__, __LINE__).FloatEquals((expected), (actual), (tol)) + +#define EXPECT_STREQ(expected, actual) \ + dart::Expect(__FILE__, __LINE__).StringEquals((expected), (actual)) + +#define EXPECT_LT(left, right) \ + dart::Expect(__FILE__, __LINE__).LessThan((left), (right)) + +#define EXPECT_LE(left, right) \ + dart::Expect(__FILE__, __LINE__).LessEqual((left), (right)) + +#define EXPECT_GT(left, right) \ + dart::Expect(__FILE__, __LINE__).GreaterThan((left), (right)) + +#define EXPECT_GE(left, right) \ + dart::Expect(__FILE__, __LINE__).GreaterEqual((left), (right)) +#endif + +// TODO(iposva): provide a better way to get extra info on an EXPECT +// fail - you suggested EXPECT_EQ(expected, actual, msg_format, +// parameters_for_msg...), I quite like the google3 method +// EXPECT_EQ(a, b) << "more stuff here...". (benl). + +#define WARN(error) \ + dart::Expect(__FILE__, __LINE__).Fail("%s", error) + +#define WARN1(format, p1) \ + dart::Expect(__FILE__, __LINE__).Fail(format, (p1)) + +#define WARN2(format, p1, p2) \ + dart::Expect(__FILE__, __LINE__).Fail(format, (p1), (p2)) + +#endif // VM_ASSERT_H_ diff --git a/runtime/vm/assert_test.cc b/runtime/vm/assert_test.cc new file mode 100644 index 00000000000..c25766a82b9 --- /dev/null +++ b/runtime/vm/assert_test.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/unit_test.h" + + +UNIT_TEST_CASE(Assert) { + ASSERT(true); + ASSERT(87 == 87); + ASSERT(42 != 87); +} + + +UNIT_TEST_CASE(Expect) { + EXPECT(true); + EXPECT(87 == 87); + EXPECT(42 != 87); + + EXPECT_EQ(0, 0); + EXPECT_EQ(42, 42); + EXPECT_EQ(true, true); + void* pointer = reinterpret_cast(42); + EXPECT_EQ(pointer, pointer); + + EXPECT_STREQ("Hello", "Hello"); + EXPECT_STREQ(42, 42); + EXPECT_STREQ(87, "87"); + + EXPECT_LT(1, 2); + EXPECT_LT(1, 1.5); + EXPECT_LT(-1.8, 3.14); + + EXPECT_LE(1, 1); + EXPECT_LE(1, 2); + EXPECT_LE(0.5, 1); + + EXPECT_GT(4, 1); + EXPECT_GT(2.3, 2.2229); + + EXPECT_GE(4, 4); + EXPECT_GE(15.3, 15.3); + EXPECT_GE(5, 3); + + EXPECT_FLOAT_EQ(15.43, 15.44, 0.01); + EXPECT_FLOAT_EQ(1.43, 1.43, 0.00); +} diff --git a/runtime/vm/ast.cc b/runtime/vm/ast.cc new file mode 100644 index 00000000000..93078625d3e --- /dev/null +++ b/runtime/vm/ast.cc @@ -0,0 +1,451 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/ast.h" +#include "vm/compiler.h" +#include "vm/dart_entry.h" +#include "vm/isolate.h" +#include "vm/object_store.h" + + +namespace dart { + +#define DEFINE_VISIT_FUNCTION(type, name) \ + void type::Visit(AstNodeVisitor* visitor) { \ + visitor->Visit##type(this); \ + } +NODE_LIST(DEFINE_VISIT_FUNCTION) +#undef DEFINE_VISIT_FUNCTION + + +#define DEFINE_NAME_FUNCTION(type, name) \ + const char* type::ShortName() const { \ + return name; \ + } +NODE_LIST(DEFINE_NAME_FUNCTION) +#undef DEFINE_NAME_FUNCTION + + +// A visitor class to collect all the nodes (including children) into an +// array. +class AstNodeCollector : public AstNodeVisitor { + public: + explicit AstNodeCollector(GrowableArray* nodes) + : nodes_(nodes) { } + +#define DEFINE_VISITOR_FUNCTION(type, name) \ + virtual void Visit##type(type* node) { \ + nodes_->Add(node); \ + node->VisitChildren(this); \ + } +NODE_LIST(DEFINE_VISITOR_FUNCTION) +#undef DEFINE_VISITOR_FUNCTION + + private: + GrowableArray* nodes_; + DISALLOW_COPY_AND_ASSIGN(AstNodeCollector); +}; + + +void SequenceNode::CollectAllNodes(GrowableArray* nodes) { + AstNodeCollector node_collector(nodes); + this->Visit(&node_collector); +} + + +void SequenceNode::VisitChildren(AstNodeVisitor* visitor) const { + for (intptr_t i = 0; i < this->length(); i++) { + NodeAt(i)->Visit(visitor); + } +} + + +void PrimaryNode::VisitChildren(AstNodeVisitor* visitor) const { +} + + +void ArgumentListNode::VisitChildren(AstNodeVisitor* visitor) const { + for (intptr_t i = 0; i < this->length(); i++) { + NodeAt(i)->Visit(visitor); + } +} + + +void ArrayNode::VisitChildren(AstNodeVisitor* visitor) const { + for (intptr_t i = 0; i < this->length(); i++) { + ElementAt(i)->Visit(visitor); + } +} + + +// TODO(srdjan): Add code for logical negation. +AstNode* LiteralNode::ApplyUnaryOp(Token::Kind unary_op_kind) { + if (unary_op_kind == Token::kSUB) { + if (literal().IsSmi()) { + Smi& smi = Smi::Handle(); + smi ^= literal().raw(); + const Instance& literal = + Instance::ZoneHandle(Integer::New(-smi.Value())); + return new LiteralNode(this->token_index(), literal); + } + if (literal().IsDouble()) { + Double& dbl = Double::Handle(); + dbl ^= literal().raw(); + // Preserve negative zero. + const Instance& literal = + Instance::ZoneHandle(Double::New(0.0 - dbl.value())); + return new LiteralNode(this->token_index(), literal); + } + } + return NULL; +} + + +bool ComparisonNode::IsKindValid() const { + return Token::IsRelationalOperator(kind_) + || Token::IsEqualityOperator(kind_) + || Token::IsInstanceofOperator(kind_); +} + + +const char* ComparisonNode::Name() const { + return Token::Str(kind_); +} + + +const Instance* ComparisonNode::EvalConstExpr() const { + const Instance* left_val = this->left()->EvalConstExpr(); + if (left_val == NULL) { + return NULL; + } + const Instance* right_val = this->right()->EvalConstExpr(); + if (right_val == NULL) { + return NULL; + } + switch (kind_) { + case Token::kLT: + case Token::kGT: + case Token::kLTE: + case Token::kGTE: + if (left_val->IsNumber() && right_val->IsNumber()) { + return &Bool::ZoneHandle(Bool::False()); + } + return NULL; + case Token::kEQ: + case Token::kNE: + case Token::kEQ_STRICT: + case Token::kNE_STRICT: + if ((left_val->IsNumber() && right_val->IsNumber()) || + (left_val->IsString() && right_val->IsString()) || + (left_val->IsBool() && right_val->IsBool())) { + return &Bool::ZoneHandle(Bool::False()); + } + return NULL; + default: + return NULL; + } + return NULL; +} + + + +bool BinaryOpNode::IsKindValid() const { + switch (kind_) { + case Token::kADD: + case Token::kSUB: + case Token::kMUL: + case Token::kDIV: + case Token::kTRUNCDIV: + case Token::kMOD: + case Token::kOR: + case Token::kAND: + case Token::kBIT_OR: + case Token::kBIT_XOR: + case Token::kBIT_AND: + case Token::kSHL: + case Token::kSAR: + case Token::kSHR: + return true; + default: + return false; + } +} + + +const char* BinaryOpNode::Name() const { + return Token::Str(kind_); +} + + +const Instance* StringConcatNode::EvalConstExpr() const { + for (int i = 0; i < values()->length(); i++) { + if (!values()->ElementAt(i)->IsLiteralNode()) { + return NULL; + } + } + // All nodes are literals, so this is a compile time constant string. + // We just return the first literal as value approximation. + return &values()->ElementAt(0)->AsLiteralNode()->literal(); +} + + +const Instance* BinaryOpNode::EvalConstExpr() const { + const Instance* left_val = this->left()->EvalConstExpr(); + if (left_val == NULL) { + return NULL; + } + if (!left_val->IsNumber() && !left_val->IsBool()) { + return NULL; + } + const Instance* right_val = this->right()->EvalConstExpr(); + if (right_val == NULL) { + return NULL; + } + switch (kind_) { + case Token::kADD: + case Token::kSUB: + case Token::kMUL: + case Token::kDIV: + case Token::kMOD: + if (left_val->IsInteger()) { + if (right_val->IsInteger()) { + return left_val; + } else if (right_val->IsNumber()) { + return right_val; + } + } else if (left_val->IsNumber() && + right_val->IsNumber()) { + return left_val; + } + return NULL; + case Token::kTRUNCDIV: + case Token::kBIT_OR: + case Token::kBIT_XOR: + case Token::kBIT_AND: + case Token::kSHL: + case Token::kSAR: + case Token::kSHR: + if (left_val->IsInteger() && + right_val->IsInteger()) { + return right_val; + } + return NULL; + case Token::kOR: + case Token::kAND: + if (left_val->IsBool() && right_val->IsBool()) { + return left_val; + } + return NULL; + default: + UNREACHABLE(); + return NULL; + } + return NULL; +} + + +AstNode* UnaryOpNode::UnaryOpOrLiteral(intptr_t token_index, + Token::Kind kind, + AstNode* operand) { + AstNode* new_operand = operand->ApplyUnaryOp(kind); + if (new_operand != NULL) { + return new_operand; + } + return new UnaryOpNode(token_index, kind, operand); +} + + +bool UnaryOpNode::IsKindValid() const { + switch (kind_) { + case Token::kADD: + case Token::kSUB: + case Token::kNOT: + case Token::kBIT_NOT: + return true; + default: + return false; + } +} + + +const Instance* UnaryOpNode::EvalConstExpr() const { + const Instance* val = this->operand()->EvalConstExpr(); + if (val == NULL) { + return NULL; + } + switch (kind_) { + case Token::kADD: + case Token::kSUB: + return val->IsNumber() ? val : NULL; + case Token::kNOT: + return val->IsBool() ? val : NULL; + case Token::kBIT_NOT: + return val->IsInteger() ? val : NULL; + default: + return NULL; + } +} + + +const char* UnaryOpNode::Name() const { + return Token::Str(kind_); +} + + +const char* IncrOpLocalNode::Name() const { + switch (kind_) { + case Token::kINCR: + return prefix_ ? "local_pre_++" : "local_post_++"; + case Token::kDECR: + return prefix_ ? "local_pre_--" : "local_post_--"; + default: + UNREACHABLE(); + return NULL; + } +} + + +const char* IncrOpInstanceFieldNode::Name() const { + switch (kind_) { + case Token::kINCR: + return prefix_ ? "instance_field_pre_++" : "instance_field_post_++"; + case Token::kDECR: + return prefix_ ? "instance_field_pre_--" : "instance_field_post_--"; + default: + UNREACHABLE(); + return NULL; + } +} + + +const char* IncrOpStaticFieldNode::Name() const { + switch (kind_) { + case Token::kINCR: + return prefix_ ? "static_field_pre_++" : "static_field_post_++"; + case Token::kDECR: + return prefix_ ? "static_field_pre_--" : "static_field_post_--"; + default: + UNREACHABLE(); + return NULL; + } +} + + +const char* JumpNode::Name() const { + return Token::Str(kind_); +} + + +const char* IncrOpIndexedNode::Name() const { + switch (kind_) { + case Token::kINCR: + return prefix_ ? "indexed_pre_++" : "indexed_post_++"; + case Token::kDECR: + return prefix_ ? "indexed_pre_--" : "indexed_post_--"; + default: + UNREACHABLE(); + return NULL; + } +} + + +AstNode* LoadLocalNode::MakeAssignmentNode(AstNode* rhs) { + if (local().is_final()) { + return NULL; + } + return new StoreLocalNode(token_index(), local(), rhs); +} + + +AstNode* LoadLocalNode::MakeIncrOpNode(intptr_t token_index, + Token::Kind kind, + bool is_prefix) { + if (local().is_final()) { + return NULL; + } + return new IncrOpLocalNode(token_index, kind, is_prefix, local()); +} + + +AstNode* LoadStaticFieldNode::MakeAssignmentNode(AstNode* rhs) { + return new StoreStaticFieldNode(token_index(), field(), rhs); +} + + +AstNode* LoadStaticFieldNode::MakeIncrOpNode(intptr_t token_index, + Token::Kind kind, + bool is_prefix) { + return new IncrOpStaticFieldNode(token_index, kind, is_prefix, field()); +} + + +AstNode* InstanceGetterNode::MakeAssignmentNode(AstNode* rhs) { + return new InstanceSetterNode(token_index(), receiver(), field_name(), rhs); +} + + +AstNode* InstanceGetterNode::MakeIncrOpNode(intptr_t token_index, + Token::Kind kind, + bool is_prefix) { + return new IncrOpInstanceFieldNode(token_index, + kind, + is_prefix, + receiver(), + field_name()); +} + + +AstNode* LoadIndexedNode::MakeAssignmentNode(AstNode* rhs) { + return new StoreIndexedNode(token_index(), array(), index_expr(), rhs); +} + + +AstNode* LoadIndexedNode::MakeIncrOpNode(intptr_t token_index, + Token::Kind kind, + bool is_prefix) { + return new IncrOpIndexedNode(token_index, + kind, + is_prefix, + array(), + index_expr()); +} + + +AstNode* StaticGetterNode::MakeAssignmentNode(AstNode* rhs) { + return new StaticSetterNode(token_index(), cls(), field_name(), rhs); +} + + +AstNode* StaticGetterNode::MakeIncrOpNode(intptr_t token_index, + Token::Kind kind, + bool is_prefix) { + return new IncrOpStaticFieldNode(token_index, + kind, + is_prefix, + cls(), + field_name()); +} + + +const Instance* StaticGetterNode::EvalConstExpr() const { + const String& getter_name = + String::Handle(Field::GetterName(this->field_name())); + const Function& getter_func = + Function::Handle(this->cls().LookupStaticFunction(getter_name)); + if (getter_func.IsNull() || !getter_func.is_const()) { + return NULL; + } + GrowableArray arguments; + const Instance& field_value = + Instance::ZoneHandle(DartEntry::InvokeStatic(getter_func, arguments)); + if (field_value.IsUnhandledException()) { + return NULL; + } + if (!field_value.IsNull()) { + return &field_value; + } + return NULL; +} + +} // namespace dart diff --git a/runtime/vm/ast.h b/runtime/vm/ast.h new file mode 100644 index 00000000000..9157a28f391 --- /dev/null +++ b/runtime/vm/ast.h @@ -0,0 +1,1918 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_AST_H_ +#define VM_AST_H_ + +#include "vm/allocation.h" +#include "vm/assert.h" +#include "vm/growable_array.h" +#include "vm/scopes.h" +#include "vm/object.h" +#include "vm/native_entry.h" +#include "vm/token.h" + +namespace dart { + +#define NODE_LIST(V) \ + V(ReturnNode, "return") \ + V(LiteralNode, "literal") \ + V(TypeNode, "type") \ + V(BinaryOpNode, "binop") \ + V(StringConcatNode, "concat") \ + V(ComparisonNode, "compare") \ + V(UnaryOpNode, "unaryop") \ + V(IncrOpLocalNode, "incr local") \ + V(IncrOpInstanceFieldNode, "incr instance field") \ + V(IncrOpStaticFieldNode, "incr static field") \ + V(IncrOpIndexedNode, "incr indexed") \ + V(ConditionalExprNode, "?:") \ + V(IfNode, "if") \ + V(SwitchNode, "switch") \ + V(CaseNode, "case") \ + V(WhileNode, "while") \ + V(DoWhileNode, "dowhile") \ + V(ForNode, "for") \ + V(JumpNode, "jump") \ + V(ArgumentListNode, "args") \ + V(ArrayNode, "array") \ + V(ClosureNode, "closure") \ + V(ImplicitClosureNode, "implicit closure") \ + V(StaticImplicitClosureNode, "static implicit closure") \ + V(InstanceCallNode, "instance call") \ + V(StaticCallNode, "static call") \ + V(ClosureCallNode, "closure call") \ + V(ConstructorCallNode, "constructor call") \ + V(InstanceGetterNode, "instance getter call") \ + V(InstanceSetterNode, "instance setter call") \ + V(StaticGetterNode, "static getter") \ + V(StaticSetterNode, "static setter") \ + V(NativeBodyNode, "native body") \ + V(PrimaryNode, "primary") \ + V(LoadLocalNode, "load local") \ + V(StoreLocalNode, "store local") \ + V(LoadInstanceFieldNode, "load field") \ + V(StoreInstanceFieldNode, "store field") \ + V(LoadStaticFieldNode, "load static field") \ + V(StoreStaticFieldNode, "store static field") \ + V(LoadIndexedNode, "load indexed") \ + V(StoreIndexedNode, "store indexed") \ + V(SequenceNode, "seq") \ + V(CatchClauseNode, "catch clause block") \ + V(TryCatchNode, "try catch block") \ + V(ThrowNode, "throw") \ + V(InlinedFinallyNode, "inlined finally") \ + + +#define DEFINE_FORWARD_DECLARATION(type, name) class type; +NODE_LIST(DEFINE_FORWARD_DECLARATION) +#undef DEFINE_FORWARD_DECLARATION + +// Forward declarations. +class CodeGenInfo; + +// Abstract class to implement an AST node visitor. An example is AstPrinter. +class AstNodeVisitor : public ValueObject { + public: + AstNodeVisitor() {} + virtual ~AstNodeVisitor() {} + +#define DEFINE_VISITOR_FUNCTION(type, name) \ + virtual void Visit##type(type* node) { } +NODE_LIST(DEFINE_VISITOR_FUNCTION) +#undef DEFINE_VISITOR_FUNCTION + + private: + DISALLOW_COPY_AND_ASSIGN(AstNodeVisitor); +}; + + +#define DECLARE_COMMON_NODE_FUNCTIONS(type) \ + virtual void Visit(AstNodeVisitor* visitor); \ + virtual const char* ShortName() const; \ + virtual bool Is##type() const { return true; } \ + virtual type* As##type() { return this; } + + +class AstNode : public ZoneAllocated { + public: + static const int kInvalidId = -1; + + explicit AstNode(intptr_t token_index) + : token_index_(token_index), + id_(GetNextId()), + collected_classes_(NULL), + info_(NULL) { + ASSERT(token_index >= 0); + } + + intptr_t token_index() const { return token_index_; } + + virtual const ZoneGrowableArray* CollectedClassesAtId( + intptr_t node_id) const { + ASSERT(id() == node_id); + return collected_classes_; + } + + virtual void SetCollectedClassesAtId( + intptr_t node_id, + const ZoneGrowableArray* value) { + ASSERT(id() == node_id); + collected_classes_ = value; + } + + virtual bool HasId(intptr_t value) const { return id_ == value; } + + intptr_t id() const { return id_; } + + void set_info(CodeGenInfo* info) { info_ = info; } + CodeGenInfo* info() const { return info_; } + +#define AST_TYPE_CHECK(type, name) \ + virtual bool Is##type() const { return false; } \ + virtual type* As##type() { return NULL; } +NODE_LIST(AST_TYPE_CHECK) +#undef AST_TYPE_CHECK + + virtual void Visit(AstNodeVisitor* visitor) = 0; + virtual void VisitChildren(AstNodeVisitor* visitor) const = 0; + virtual const char* ShortName() const = 0; + + // 'ShortName' is predefined for each AstNode and is the default + // implementation of "Name()". Each AST node can override the function + // "Name" to do more complex name composition. + virtual const char* Name() const { + return ShortName(); + } + + // Convert the node into an assignment node using the rhs which is passed in, + // this is typically used for converting nodes like LoadLocalNode, + // LoadStaticFieldNode, InstanceGetterNode etc. which were created during + // parsing as the assignment context was not known yet at that time. + virtual AstNode* MakeAssignmentNode(AstNode* rhs) { + return NULL; // By default all nodes are not assignable. + } + + // Return NULL if 'unary_op_kind' can't be applied. + virtual AstNode* ApplyUnaryOp(Token::Kind unary_op_kind) { + return NULL; + } + + // Creates a an IncrOpXXXNode that corresponds to this node type, e.g., + // LoadLocalNode creates the appropriate IncrOpLocalNode + virtual AstNode* MakeIncrOpNode(intptr_t token_index, + Token::Kind kind, + bool is_prefix) { + return NULL; + } + + // Analyzes an expression to determine whether it is a compile time + // constant or not. Returns NULL if the expression is not a compile time + // constant. Otherwise, the return value is an approximation of the + // actual value of the const expression. The type of the returned value + // corresponds to the type of the const expression and is either + // Number, Integer, String, Bool, or anything else (not a subtype of + // the former). + virtual const Instance* EvalConstExpr() const { return NULL; } + + protected: + void set_collected_classes(const ZoneGrowableArray* value) { + collected_classes_ = value; + } + + const ZoneGrowableArray* collected_classes() const { + return collected_classes_; + } + + static intptr_t GetNextId() { + Isolate* isolate = Isolate::Current(); + intptr_t tmp = isolate->ast_node_id(); + isolate->set_ast_node_id(tmp + 1); + return tmp; + } + + private: + const intptr_t token_index_; + // Unique id per function compiled, used to match AST node to a PC. + const intptr_t id_; + // Possible classes of the node, collected using inline caches. + // Inline caches contain all encountered receiver classes for that node. + const ZoneGrowableArray* collected_classes_; + // Used by optimizing compiler. + CodeGenInfo* info_; + DISALLOW_COPY_AND_ASSIGN(AstNode); +}; + + +class SequenceNode : public AstNode { + public: + SequenceNode(intptr_t token_index, LocalScope* scope) + : AstNode(token_index), + scope_(scope), + nodes_(4), + label_(NULL) { + ASSERT(scope_ != NULL); + } + + LocalScope* scope() const { return scope_; } + + SourceLabel* label() const { return label_; } + void set_label(SourceLabel* value) { label_ = value; } + + void VisitChildren(AstNodeVisitor* visitor) const; + + void Add(AstNode* node) { nodes_.Add(node); } + intptr_t length() const { return nodes_.length(); } + AstNode* NodeAt(intptr_t index) const { return nodes_[index]; } + + DECLARE_COMMON_NODE_FUNCTIONS(SequenceNode); + + // Collects all nodes accessible from this sequence node into array 'nodes'. + void CollectAllNodes(GrowableArray* nodes); + + private: + LocalScope* scope_; + GrowableArray nodes_; + SourceLabel* label_; + + DISALLOW_COPY_AND_ASSIGN(SequenceNode); +}; + + +class ArgumentListNode : public AstNode { + public: + explicit ArgumentListNode(intptr_t token_index) + : AstNode(token_index), + nodes_(4), + names_(Array::ZoneHandle()) { + } + + void VisitChildren(AstNodeVisitor* visitor) const; + + void Add(AstNode* node) { + nodes_.Add(node); + } + intptr_t length() const { return nodes_.length(); } + AstNode* NodeAt(intptr_t index) const { return nodes_[index]; } + void SetNodeAt(intptr_t index, AstNode* node) { nodes_[index] = node; } + const Array& names() const { + return names_; + } + void set_names(const Array& names) { + names_ = names.raw(); + } + + DECLARE_COMMON_NODE_FUNCTIONS(ArgumentListNode); + + private: + GrowableArray nodes_; + Array& names_; + + DISALLOW_COPY_AND_ASSIGN(ArgumentListNode); +}; + + +class ArrayNode : public AstNode { + public: + ArrayNode(intptr_t token_index, const TypeArguments& type_arguments) + : AstNode(token_index), + type_arguments_(type_arguments), + elements_(4) { + ASSERT(type_arguments_.IsZoneHandle()); + ASSERT(type_arguments.IsNull() || type_arguments_.IsInstantiated()); + } + + void VisitChildren(AstNodeVisitor* visitor) const; + + intptr_t length() const { return elements_.length(); } + + AstNode* ElementAt(intptr_t index) const { return elements_[index]; } + void SetElementAt(intptr_t index, AstNode* value) { + elements_[index] = value; + } + void AddElement(AstNode* expr) { elements_.Add(expr); } + + const TypeArguments& type_arguments() const { return type_arguments_; } + + DECLARE_COMMON_NODE_FUNCTIONS(ArrayNode); + + private: + const TypeArguments& type_arguments_; + GrowableArray elements_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ArrayNode); +}; + + +class LiteralNode : public AstNode { + public: + LiteralNode(intptr_t token_index, const Instance& literal) + : AstNode(token_index), literal_(literal) { + ASSERT(literal.IsZoneHandle()); +#if defined(DEBUG) + if (literal.IsString()) { + String& str = String::Handle(); + str ^= literal.raw(); + ASSERT(str.IsSymbol()); + } +#endif // defined(DEBUG) + ASSERT(literal.IsNull() || Class::Handle(literal.clazz()).is_finalized()); + } + + const Instance& literal() const { return literal_; } + + virtual const Instance* EvalConstExpr() const { + return &literal(); + } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { } + + virtual AstNode* ApplyUnaryOp(Token::Kind unary_op_kind); + + DECLARE_COMMON_NODE_FUNCTIONS(LiteralNode); + + private: + const Instance& literal_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(LiteralNode); +}; + + +class TypeNode : public AstNode { + public: + TypeNode(intptr_t token_index, const Type& type) + : AstNode(token_index), type_(type) { + ASSERT(type.IsZoneHandle()); + ASSERT(!type.IsNull()); + ASSERT(type.IsFinalized()); + } + + const Type& type() const { return type_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { } + + DECLARE_COMMON_NODE_FUNCTIONS(TypeNode); + + private: + const Type& type_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(TypeNode); +}; + + +class ClosureNode : public AstNode { + public: + ClosureNode(intptr_t token_index, const Function& function, LocalScope* scope) + : AstNode(token_index), function_(function), scope_(scope) { + ASSERT(function.IsZoneHandle()); + ASSERT(scope_ != NULL); + } + + const Function& function() const { return function_; } + LocalScope* scope() const { return scope_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { } + + DECLARE_COMMON_NODE_FUNCTIONS(ClosureNode); + + private: + const Function& function_; + LocalScope* scope_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ClosureNode); +}; + + +class StaticImplicitClosureNode : public AstNode { + public: + StaticImplicitClosureNode(intptr_t token_index, const Function& function) + : AstNode(token_index), function_(function) { + ASSERT(function.IsZoneHandle()); + } + + const Function& function() const { return function_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { } + + DECLARE_COMMON_NODE_FUNCTIONS(StaticImplicitClosureNode); + + private: + const Function& function_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(StaticImplicitClosureNode); +}; + + +class ImplicitClosureNode : public AstNode { + public: + ImplicitClosureNode(intptr_t token_index, + const Function& function, + AstNode* receiver) + : AstNode(token_index), function_(function), receiver_(receiver) { + ASSERT(function.IsZoneHandle()); + } + + const Function& function() const { return function_; } + AstNode* receiver() const { return receiver_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + if (receiver() != NULL) { + receiver()->Visit(visitor); + } + } + + DECLARE_COMMON_NODE_FUNCTIONS(ImplicitClosureNode); + + private: + const Function& function_; + AstNode* receiver_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ImplicitClosureNode); +}; + + +// Primary nodes hold identifiers or values (library, class or function) +// resolved from an identifier. Primary nodes should not ever make it to the +// code generation phase as they will be transformed into the correct call or +// field access nodes. +class PrimaryNode : public AstNode { + public: + PrimaryNode(intptr_t token_index, const Object& primary) + : AstNode(token_index), primary_(primary) { + ASSERT(primary.IsZoneHandle()); + } + + const Object& primary() const { return primary_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const; + + DECLARE_COMMON_NODE_FUNCTIONS(PrimaryNode); + + private: + const Object& primary_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(PrimaryNode); +}; + + +class ReturnNode : public AstNode { + public: + // Return from a void function returns the null object. + explicit ReturnNode(intptr_t token_index) + : AstNode(token_index), + value_(new LiteralNode(token_index, Instance::ZoneHandle())), + inlined_finally_list_() { } + // Return from a non-void function. + ReturnNode(intptr_t token_index, + AstNode* value) + : AstNode(token_index), value_(value), inlined_finally_list_() { + ASSERT(value != NULL); + } + + AstNode* value() const { return value_; } + + intptr_t inlined_finally_list_length() const { + return inlined_finally_list_.length(); + } + InlinedFinallyNode* InlinedFinallyNodeAt(intptr_t index) const { + return inlined_finally_list_[index]; + } + void AddInlinedFinallyNode(InlinedFinallyNode* finally_node) { + inlined_finally_list_.Add(finally_node); + } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + if (value() != NULL) { + value()->Visit(visitor); + } + } + + DECLARE_COMMON_NODE_FUNCTIONS(ReturnNode); + + private: + AstNode* value_; + GrowableArray inlined_finally_list_; + + DISALLOW_COPY_AND_ASSIGN(ReturnNode); +}; + + +class ComparisonNode : public AstNode { + public: + ComparisonNode(intptr_t token_index, + Token::Kind kind, + AstNode* left, + AstNode* right) + : AstNode(token_index), kind_(kind), left_(left), right_(right) { + ASSERT(left_ != NULL); + ASSERT(right_ != NULL); + ASSERT(IsKindValid()); + } + + Token::Kind kind() const { return kind_; } + AstNode* left() const { return left_; } + AstNode* right() const { return right_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + left()->Visit(visitor); + right()->Visit(visitor); + } + + virtual const char* Name() const; + virtual const Instance* EvalConstExpr() const; + + DECLARE_COMMON_NODE_FUNCTIONS(ComparisonNode); + + private: + const Token::Kind kind_; + AstNode* left_; + AstNode* right_; + + bool IsKindValid() const; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ComparisonNode); +}; + + +class BinaryOpNode : public AstNode { + public: + BinaryOpNode(intptr_t token_index, + Token::Kind kind, + AstNode* left, + AstNode* right) + : AstNode(token_index), kind_(kind), left_(left), right_(right) { + ASSERT(left_ != NULL); + ASSERT(right_ != NULL); + ASSERT(IsKindValid()); + } + + Token::Kind kind() const { return kind_; } + AstNode* left() const { return left_; } + AstNode* right() const { return right_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + left()->Visit(visitor); + right()->Visit(visitor); + } + + virtual const char* Name() const; + virtual const Instance* EvalConstExpr() const; + + DECLARE_COMMON_NODE_FUNCTIONS(BinaryOpNode); + + private: + const Token::Kind kind_; + AstNode* left_; + AstNode* right_; + + bool IsKindValid() const; + + DISALLOW_IMPLICIT_CONSTRUCTORS(BinaryOpNode); +}; + + +class StringConcatNode : public AstNode { + public: + explicit StringConcatNode(intptr_t token_index) + : AstNode(token_index), + values_(new ArrayNode(token_index, TypeArguments::ZoneHandle())) { + } + + ArrayNode* values() const { return values_; } + + virtual const Instance* EvalConstExpr() const; + + void AddExpr(AstNode* expr) const { + values_->AddElement(expr); + } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + values_->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(StringConcatNode); + + private: + ArrayNode* values_; + DISALLOW_IMPLICIT_CONSTRUCTORS(StringConcatNode); +}; + + +class UnaryOpNode : public AstNode { + public: + // Returns optimized version, e.g., for ('-' '1') ('-1') literal is returned. + static AstNode* UnaryOpOrLiteral(intptr_t token_index, + Token::Kind kind, + AstNode* operand); + UnaryOpNode(intptr_t token_index, + Token::Kind kind, + AstNode* operand) + : AstNode(token_index), kind_(kind), operand_(operand) { + ASSERT(operand_ != NULL); + ASSERT(IsKindValid()); + } + + Token::Kind kind() const { return kind_; } + AstNode* operand() const { return operand_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + operand()->Visit(visitor); + } + + virtual const char* Name() const; + virtual const Instance* EvalConstExpr() const; + + DECLARE_COMMON_NODE_FUNCTIONS(UnaryOpNode); + + private: + const Token::Kind kind_; + AstNode* operand_; + + bool IsKindValid() const; + + DISALLOW_IMPLICIT_CONSTRUCTORS(UnaryOpNode); +}; + + +class IncrOpLocalNode : public AstNode { + public: + IncrOpLocalNode(intptr_t token_index, + Token::Kind kind, + bool prefix, + const LocalVariable& local) + : AstNode(token_index), kind_(kind), prefix_(prefix), local_(local) { + ASSERT(kind_ == Token::kINCR || kind_ == Token::kDECR); + } + + Token::Kind kind() const { return kind_; } + bool prefix() const { return prefix_; } + const LocalVariable& local() const { return local_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const {} + + virtual const char* Name() const; + + DECLARE_COMMON_NODE_FUNCTIONS(IncrOpLocalNode); + + private: + const Token::Kind kind_; + const bool prefix_; + const LocalVariable& local_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(IncrOpLocalNode); +}; + + +class IncrOpInstanceFieldNode : public AstNode { + public: + IncrOpInstanceFieldNode(intptr_t token_index, + Token::Kind kind, + bool prefix, + AstNode* receiver, + const String& field_name) + : AstNode(token_index), + kind_(kind), + prefix_(prefix), + receiver_(receiver), + field_name_(field_name), + operator_id_(AstNode::GetNextId()), + setter_id_(AstNode::GetNextId()), + operator_collected_classes_(NULL), + setter_collected_classes_(NULL) { + ASSERT(receiver_ != NULL); + ASSERT(field_name_.IsZoneHandle()); + ASSERT(kind_ == Token::kINCR || kind_ == Token::kDECR); + } + + Token::Kind kind() const { return kind_; } + bool prefix() const { return prefix_; } + AstNode* receiver() const { return receiver_; } + const String& field_name() const { return field_name_; } + + intptr_t getter_id() const { return id(); } + intptr_t operator_id() const { return operator_id_; } + intptr_t setter_id() const { return setter_id_; } + + virtual bool HasId(intptr_t value) const { + return (getter_id() == value) || + (operator_id() == value) || + (setter_id() == value); + } + + virtual const ZoneGrowableArray* CollectedClassesAtId( + intptr_t node_id) const { + ASSERT(HasId(node_id)); + if (node_id == getter_id()) { + return collected_classes(); + } else if (node_id == operator_id()) { + return operator_collected_classes_; + } else { + ASSERT(node_id == setter_id()); + return setter_collected_classes_; + } + } + + virtual void SetCollectedClassesAtId( + intptr_t node_id, + const ZoneGrowableArray* value) { + ASSERT(HasId(node_id)); + if (node_id == getter_id()) { + set_collected_classes(value); + } else if (node_id == operator_id()) { + operator_collected_classes_ = value; + } else { + ASSERT(node_id == setter_id()); + setter_collected_classes_ = value; + } + } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + receiver()->Visit(visitor); + } + + virtual const char* Name() const; + + DECLARE_COMMON_NODE_FUNCTIONS(IncrOpInstanceFieldNode); + + private: + const Token::Kind kind_; + const bool prefix_; + AstNode* receiver_; + const String& field_name_; + const intptr_t operator_id_; + const intptr_t setter_id_; + const ZoneGrowableArray* operator_collected_classes_; + const ZoneGrowableArray* setter_collected_classes_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(IncrOpInstanceFieldNode); +}; + + +class IncrOpStaticFieldNode : public AstNode { + public: + // Access the static field via a call to static getter. + IncrOpStaticFieldNode(intptr_t token_index, + Token::Kind kind, + bool prefix, + const Class& field_class, + const String& field_name) + : AstNode(token_index), + kind_(kind), + prefix_(prefix), + field_class_(field_class), + field_name_(field_name), + field_(Field::ZoneHandle()) { + ASSERT(field_class_.IsZoneHandle()); + ASSERT(field_name_.IsZoneHandle()); + ASSERT(kind_ == Token::kINCR || kind_ == Token::kDECR); + } + + // Access the static field directly. + IncrOpStaticFieldNode(intptr_t token_index, + Token::Kind kind, + bool prefix, + const Field& field) + : AstNode(token_index), + kind_(kind), + prefix_(prefix), + field_class_(Class::ZoneHandle()), + field_name_(String::ZoneHandle()), + field_(field) { + ASSERT(field_.IsZoneHandle()); + ASSERT(kind_ == Token::kINCR || kind_ == Token::kDECR); + } + + Token::Kind kind() const { return kind_; } + bool prefix() const { return prefix_; } + const Class& field_class() const { return field_class_; } + const String& field_name() const { return field_name_; } + const Field& field() const { return field_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { } + + virtual const char* Name() const; + + DECLARE_COMMON_NODE_FUNCTIONS(IncrOpStaticFieldNode); + + private: + const Token::Kind kind_; + const bool prefix_; + const Class& field_class_; + const String& field_name_; + const Field& field_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(IncrOpStaticFieldNode); +}; + + +class IncrOpIndexedNode : public AstNode { + public: + IncrOpIndexedNode(intptr_t token_index, + Token::Kind kind, + bool prefix, + AstNode* array, + AstNode* index) + : AstNode(token_index), + kind_(kind), + prefix_(prefix), + array_(array), + index_(index), + operator_id_(AstNode::GetNextId()), + store_id_(AstNode::GetNextId()), + operator_collected_classes_(NULL), + store_collected_classes_(NULL) { + ASSERT(kind_ == Token::kINCR || kind_ == Token::kDECR); + ASSERT(array_ != NULL); + ASSERT(index_ != NULL); + } + + Token::Kind kind() const { return kind_; } + bool prefix() const { return prefix_; } + AstNode* array() const { return array_; } + AstNode* index() const { return index_; } + + intptr_t load_id() const { return id(); } + intptr_t operator_id() const { return operator_id_; } + intptr_t store_id() const { return store_id_; } + + virtual bool HasId(intptr_t value) const { + return (load_id() == value) || + (operator_id() == value) || + (store_id() == value); + } + + virtual const ZoneGrowableArray* CollectedClassesAtId( + intptr_t node_id) const { + ASSERT(HasId(node_id)); + if (node_id == load_id()) { + return collected_classes(); + } else if (node_id == operator_id()) { + return operator_collected_classes_; + } else { + ASSERT(node_id == store_id()); + return store_collected_classes_; + } + } + + virtual void SetCollectedClassesAtId( + intptr_t node_id, + const ZoneGrowableArray* value) { + ASSERT(HasId(node_id)); + if (node_id == load_id()) { + set_collected_classes(value); + } else if (node_id == operator_id()) { + operator_collected_classes_ = value; + } else { + ASSERT(node_id == store_id()); + store_collected_classes_ = value; + } + } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + array()->Visit(visitor); + index()->Visit(visitor); + } + + virtual const char* Name() const; + + DECLARE_COMMON_NODE_FUNCTIONS(IncrOpIndexedNode); + + private: + const Token::Kind kind_; + const bool prefix_; + AstNode* array_; + AstNode* index_; + const intptr_t operator_id_; + const intptr_t store_id_; + const ZoneGrowableArray* operator_collected_classes_; + const ZoneGrowableArray* store_collected_classes_; +}; + + +class ConditionalExprNode : public AstNode { + public: + ConditionalExprNode(intptr_t token_index, + AstNode* condition, + AstNode* true_expr, + AstNode* false_expr) + : AstNode(token_index), + condition_(condition), + true_expr_(true_expr), + false_expr_(false_expr) { + ASSERT(condition_ != NULL); + ASSERT(true_expr_ != NULL); + ASSERT(false_expr_ != NULL); + } + + AstNode* condition() const { return condition_; } + AstNode* true_expr() const { return true_expr_; } + AstNode* false_expr() const { return false_expr_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + condition()->Visit(visitor); + true_expr()->Visit(visitor); + false_expr()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(ConditionalExprNode); + + private: + AstNode* condition_; + AstNode* true_expr_; + AstNode* false_expr_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ConditionalExprNode); +}; + + +class IfNode : public AstNode { + public: + IfNode(intptr_t token_index, + AstNode* condition, + SequenceNode* true_branch, + SequenceNode* false_branch) + : AstNode(token_index), + condition_(condition), + true_branch_(true_branch), + false_branch_(false_branch) { + ASSERT(condition_ != NULL); + } + + AstNode* condition() const { return condition_; } + SequenceNode* true_branch() const { return true_branch_; } + SequenceNode* false_branch() const { return false_branch_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + condition()->Visit(visitor); + true_branch()->Visit(visitor); + if (false_branch() != NULL) { + false_branch()->Visit(visitor); + } + } + + DECLARE_COMMON_NODE_FUNCTIONS(IfNode); + + private: + AstNode* condition_; + SequenceNode* true_branch_; + SequenceNode* false_branch_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(IfNode); +}; + + +class CaseNode : public AstNode { + public: + CaseNode(intptr_t token_index, + SourceLabel* label, + SequenceNode* case_expressions, + bool contains_default, + LocalVariable* switch_expr_value, + SequenceNode* statements) + : AstNode(token_index), + label_(label), + case_expressions_(case_expressions), + contains_default_(contains_default), + switch_expr_value_(switch_expr_value), + statements_(statements) { + // label may be NULL. + ASSERT(case_expressions_ != NULL); + ASSERT(switch_expr_value_ != NULL); + ASSERT(statements_ != NULL); + } + + SourceLabel* label() const { return label_; } + SequenceNode* case_expressions() const { return case_expressions_; } + bool contains_default() const { return contains_default_; } + LocalVariable* switch_expr_value() const { return switch_expr_value_; } + SequenceNode* statements() const { return statements_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + case_expressions()->Visit(visitor); + statements()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(CaseNode); + + private: + SourceLabel* label_; + SequenceNode* case_expressions_; + bool contains_default_; + LocalVariable* switch_expr_value_; + SequenceNode* statements_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(CaseNode); +}; + + +class SwitchNode : public AstNode { + public: + SwitchNode(intptr_t token_index, + SourceLabel* label, + SequenceNode* body) + : AstNode(token_index), + label_(label), + body_(body) { + ASSERT(label_ != NULL); + ASSERT(body_ != NULL); + } + + SourceLabel* label() const { return label_; } + AstNode* body() const { return body_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + body()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(SwitchNode); + + private: + SourceLabel* label_; + AstNode* body_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(SwitchNode); +}; + + +class WhileNode : public AstNode { + public: + WhileNode(intptr_t token_index, + SourceLabel* label, + AstNode* condition, + SequenceNode* body) + : AstNode(token_index), + label_(label), + condition_(condition), + body_(body) { + ASSERT(label_ != NULL); + ASSERT(condition_ != NULL); + ASSERT(body_ != NULL); + } + + SourceLabel* label() const { return label_; } + AstNode* condition() const { return condition_; } + SequenceNode* body() const { return body_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + condition()->Visit(visitor); + body()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(WhileNode); + + private: + SourceLabel* label_; + AstNode* condition_; + SequenceNode* body_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(WhileNode); +}; + + +class DoWhileNode : public AstNode { + public: + DoWhileNode(intptr_t token_index, + SourceLabel* label, + AstNode* condition, + SequenceNode* body) + : AstNode(token_index), + label_(label), + condition_(condition), + body_(body) { + ASSERT(label_ != NULL); + ASSERT(condition_ != NULL); + ASSERT(body_ != NULL); + } + + SourceLabel* label() const { return label_; } + AstNode* condition() const { return condition_; } + SequenceNode* body() const { return body_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + body()->Visit(visitor); + condition()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(DoWhileNode); + + private: + SourceLabel* label_; + AstNode* condition_; + SequenceNode* body_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DoWhileNode); +}; + + +// initializer, condition, increment expressions can be NULL. +class ForNode : public AstNode { + public: + ForNode(intptr_t token_index, + SourceLabel* label, + SequenceNode* initializer, + AstNode* condition, + SequenceNode* increment, + SequenceNode* body) + : AstNode(token_index), + label_(label), + initializer_(initializer), + condition_(condition), + increment_(increment), + body_(body) { + ASSERT(label_ != NULL); + ASSERT(initializer_ != NULL); + ASSERT(increment_ != NULL); + ASSERT(body_ != NULL); + } + + SourceLabel* label() const { return label_; } + SequenceNode* initializer() const { return initializer_; } + AstNode* condition() const { return condition_; } + SequenceNode* increment() const { return increment_; } + SequenceNode* body() const { return body_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + initializer()->Visit(visitor); + if (condition() != NULL) { + condition()->Visit(visitor); + } + increment()->Visit(visitor); + body()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(ForNode); + + private: + SourceLabel* label_; + SequenceNode* initializer_; + AstNode* condition_; + SequenceNode* increment_; + SequenceNode* body_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ForNode); +}; + + +class JumpNode : public AstNode { + public: + JumpNode(intptr_t token_index, + Token::Kind kind, + SourceLabel* label) + : AstNode(token_index), + kind_(kind), + label_(label), + inlined_finally_list_(NULL) { + ASSERT(label_ != NULL); + ASSERT(kind_ == Token::kBREAK || kind_ == Token::kCONTINUE); + } + + SourceLabel* label() const { return label_; } + Token::Kind kind() const { return kind_; } + + intptr_t inlined_finally_list_length() const { + return inlined_finally_list_.length(); + } + InlinedFinallyNode* InlinedFinallyNodeAt(intptr_t index) const { + return inlined_finally_list_[index]; + } + void AddInlinedFinallyNode(InlinedFinallyNode* finally_node) { + inlined_finally_list_.Add(finally_node); + } + + virtual const char* Name() const; + + virtual void VisitChildren(AstNodeVisitor* visitor) const { } + + DECLARE_COMMON_NODE_FUNCTIONS(JumpNode); + + private: + Token::Kind kind_; + SourceLabel* label_; + GrowableArray inlined_finally_list_; + DISALLOW_IMPLICIT_CONSTRUCTORS(JumpNode); +}; + + +class LoadLocalNode : public AstNode { + public: + LoadLocalNode(intptr_t token_index, const LocalVariable& local) + : AstNode(token_index), local_(local) { } + + const LocalVariable& local() const { return local_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { } + + virtual AstNode* MakeAssignmentNode(AstNode* rhs); + + virtual AstNode* MakeIncrOpNode(intptr_t token_index, + Token::Kind kind, + bool is_prefix); + + DECLARE_COMMON_NODE_FUNCTIONS(LoadLocalNode); + + private: + const LocalVariable& local_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(LoadLocalNode); +}; + + +class StoreLocalNode : public AstNode { + public: + StoreLocalNode(intptr_t token_index, + const LocalVariable& local, + AstNode* value) + : AstNode(token_index), local_(local), value_(value) { + ASSERT(value_ != NULL); + } + + const LocalVariable& local() const { return local_; } + AstNode* value() const { return value_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + value()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(StoreLocalNode); + + private: + const LocalVariable& local_; + AstNode* value_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(StoreLocalNode); +}; + + + +class LoadInstanceFieldNode : public AstNode { + public: + LoadInstanceFieldNode(intptr_t token_index, + AstNode* instance, + const Field& field) + : AstNode(token_index), instance_(instance), field_(field) { + ASSERT(instance_ != NULL); + ASSERT(field.IsZoneHandle()); + } + + AstNode* instance() const { return instance_; } + const Field& field() const { return field_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + instance()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(LoadInstanceFieldNode); + + private: + AstNode* instance_; + const Field& field_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(LoadInstanceFieldNode); +}; + + +class StoreInstanceFieldNode : public AstNode { + public: + StoreInstanceFieldNode(intptr_t token_index, + AstNode* instance, + const Field& field, + AstNode* value) + : AstNode(token_index), + instance_(instance), + field_(field), + value_(value) { + ASSERT(instance_ != NULL); + ASSERT(field.IsZoneHandle()); + ASSERT(value_ != NULL); + } + + AstNode* instance() const { return instance_; } + const Field& field() const { return field_; } + AstNode* value() const { return value_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + instance()->Visit(visitor); + value()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(StoreInstanceFieldNode); + + private: + AstNode* instance_; + const Field& field_; + AstNode* value_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(StoreInstanceFieldNode); +}; + + +class LoadStaticFieldNode : public AstNode { + public: + LoadStaticFieldNode(intptr_t token_index, const Field& field) + : AstNode(token_index), field_(field) { + ASSERT(field.IsZoneHandle()); + } + + const Field& field() const { return field_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { } + + virtual AstNode* MakeAssignmentNode(AstNode* rhs); + + virtual AstNode* MakeIncrOpNode(intptr_t token_index, + Token::Kind kind, + bool is_prefix); + + virtual const Instance* EvalConstExpr() const { + ASSERT(field_.is_static()); + return field_.is_final() ? &Instance::ZoneHandle(field_.value()) : NULL; + } + + DECLARE_COMMON_NODE_FUNCTIONS(LoadStaticFieldNode); + + private: + const Field& field_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(LoadStaticFieldNode); +}; + + +class StoreStaticFieldNode : public AstNode { + public: + StoreStaticFieldNode(intptr_t token_index, const Field& field, AstNode* value) + : AstNode(token_index), field_(field), value_(value) { + ASSERT(field.IsZoneHandle()); + ASSERT(value_ != NULL); + } + + const Field& field() const { return field_; } + AstNode* value() const { return value_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + value()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(StoreStaticFieldNode); + + private: + const Field& field_; + AstNode* value_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(StoreStaticFieldNode); +}; + + +class LoadIndexedNode : public AstNode { + public: + LoadIndexedNode(intptr_t token_index, AstNode* array, AstNode* index) + : AstNode(token_index), array_(array), index_expr_(index) { + ASSERT(array != NULL); + ASSERT(index != NULL); + } + + AstNode* array() const { return array_; } + AstNode* index_expr() const { return index_expr_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + array()->Visit(visitor); + index_expr()->Visit(visitor); + } + + virtual AstNode* MakeAssignmentNode(AstNode* rhs); + + virtual AstNode* MakeIncrOpNode(intptr_t token_index, + Token::Kind kind, + bool is_prefix); + + DECLARE_COMMON_NODE_FUNCTIONS(LoadIndexedNode); + + private: + AstNode* array_; + AstNode* index_expr_; + DISALLOW_IMPLICIT_CONSTRUCTORS(LoadIndexedNode); +}; + + +class StoreIndexedNode : public AstNode { + public: + StoreIndexedNode(intptr_t token_index, + AstNode* array, AstNode* index, AstNode* value) + : AstNode(token_index), array_(array), index_expr_(index), value_(value) { + ASSERT(array != NULL); + ASSERT(index != NULL); + ASSERT(value != NULL); + } + + AstNode* array() const { return array_; } + AstNode* index_expr() const { return index_expr_; } + AstNode* value() const { return value_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + array()->Visit(visitor); + index_expr()->Visit(visitor); + value()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(StoreIndexedNode); + + private: + AstNode* array_; + AstNode* index_expr_; + AstNode* value_; + DISALLOW_IMPLICIT_CONSTRUCTORS(StoreIndexedNode); +}; + + +class InstanceCallNode : public AstNode { + public: + InstanceCallNode(intptr_t token_index, + AstNode* receiver, + const String& function_name, + ArgumentListNode* arguments) + : AstNode(token_index), + receiver_(receiver), + function_name_(function_name), + arguments_(arguments) { + ASSERT(receiver_ != NULL); + ASSERT(function_name_.IsZoneHandle()); + ASSERT(function_name_.IsSymbol()); + ASSERT(arguments_ != NULL); + } + + AstNode* receiver() const { return receiver_; } + const String& function_name() const { return function_name_; } + ArgumentListNode* arguments() const { return arguments_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + receiver()->Visit(visitor); + arguments()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(InstanceCallNode); + + private: + AstNode* receiver_; + const String& function_name_; + ArgumentListNode* arguments_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(InstanceCallNode); +}; + + +class InstanceGetterNode : public AstNode { + public: + InstanceGetterNode(intptr_t token_index, + AstNode* receiver, + const String& field_name) + : AstNode(token_index), + receiver_(receiver), + field_name_(field_name) { + ASSERT(receiver_ != NULL); + ASSERT(field_name_.IsZoneHandle()); + ASSERT(field_name_.IsSymbol()); + } + + AstNode* receiver() const { return receiver_; } + const String& field_name() const { return field_name_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + receiver()->Visit(visitor); + } + + virtual AstNode* MakeAssignmentNode(AstNode* rhs); + + virtual AstNode* MakeIncrOpNode(intptr_t token_index, + Token::Kind kind, + bool is_prefix); + + DECLARE_COMMON_NODE_FUNCTIONS(InstanceGetterNode); + + private: + AstNode* receiver_; + const String& field_name_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(InstanceGetterNode); +}; + + +class InstanceSetterNode : public AstNode { + public: + InstanceSetterNode(intptr_t token_index, + AstNode* receiver, + const String& field_name, + AstNode* value) + : AstNode(token_index), + receiver_(receiver), + field_name_(field_name), + value_(value) { + ASSERT(receiver_ != NULL); + ASSERT(value_ != NULL); + ASSERT(field_name_.IsZoneHandle()); + ASSERT(field_name_.IsSymbol()); + } + + AstNode* receiver() const { return receiver_; } + const String& field_name() const { return field_name_; } + AstNode* value() const { return value_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + receiver()->Visit(visitor); + value()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(InstanceSetterNode); + + private: + AstNode* receiver_; + const String& field_name_; + AstNode* value_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(InstanceSetterNode); +}; + + +class StaticGetterNode : public AstNode { + public: + StaticGetterNode(intptr_t token_index, + const Class& cls, + const String& field_name) + : AstNode(token_index), + cls_(cls), + field_name_(field_name) { + ASSERT(cls_.IsZoneHandle()); + ASSERT(field_name_.IsZoneHandle()); + ASSERT(field_name_.IsSymbol()); + } + + const Class& cls() const { return cls_; } + const String& field_name() const { return field_name_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { } + + virtual AstNode* MakeAssignmentNode(AstNode* rhs); + + virtual AstNode* MakeIncrOpNode(intptr_t token_index, + Token::Kind kind, + bool is_prefix); + + virtual const Instance* EvalConstExpr() const; + + DECLARE_COMMON_NODE_FUNCTIONS(StaticGetterNode); + + private: + const Class& cls_; + const String& field_name_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(StaticGetterNode); +}; + + +class StaticSetterNode : public AstNode { + public: + StaticSetterNode(intptr_t token_index, + const Class& cls, + const String& field_name, + AstNode* value) + : AstNode(token_index), + cls_(cls), + field_name_(field_name), + value_(value) { + ASSERT(cls_.IsZoneHandle()); + ASSERT(field_name_.IsZoneHandle()); + ASSERT(value != NULL); + } + + const Class& cls() const { return cls_; } + const String& field_name() const { return field_name_; } + AstNode* value() const { return value_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + value()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(StaticSetterNode); + + private: + const Class& cls_; + const String& field_name_; + AstNode* value_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(StaticSetterNode); +}; + + +class StaticCallNode : public AstNode { + public: + StaticCallNode(intptr_t token_index, + const Function& function, + ArgumentListNode* arguments) + : AstNode(token_index), + function_(function), + arguments_(arguments) { + ASSERT(function.IsZoneHandle()); + ASSERT(arguments_ != NULL); + } + + const Function& function() const { return function_; } + ArgumentListNode* arguments() const { return arguments_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + arguments()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(StaticCallNode); + + private: + const Function& function_; + ArgumentListNode* arguments_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(StaticCallNode); +}; + + +class ClosureCallNode : public AstNode { + public: + ClosureCallNode(intptr_t token_index, + AstNode* closure, + ArgumentListNode* arguments) + : AstNode(token_index), + closure_(closure), + arguments_(arguments) { + ASSERT(closure_ != NULL); + ASSERT(arguments_ != NULL); + } + + AstNode* closure() const { return closure_; } + ArgumentListNode* arguments() const { return arguments_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + closure()->Visit(visitor); + arguments()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(ClosureCallNode); + + private: + AstNode* closure_; + ArgumentListNode* arguments_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ClosureCallNode); +}; + + +// There are two kinds of constructor calls: factory calls and constructor +// calls, distinguishable by constructor.IsFactory(). +// +// Constructor calls implicitly allocate an object of class constructor.owner(), +// possibly parameterized by type_arguments, which may still be uninstantiated. +// For example, if the type argument T in 'new A()' is not known at compile +// time, it needs to be instantiated at run time. The instantiator and its +// instantiator_class are used to instantiate uninstantiated type arguments +// at run time, as explained below. +// +// Factory calls do not implicitly allocate an object, but receive an implicit +// type argument vector as first parameter, which may still be uninstantiated. +// As in constructor calls, the instantiator and its instantiator_class are used +// to instantiate uninstantiated type arguments at run time. +// +// If the caller to the constructor or to the factory is an instance function, +// the instantiator is the receiver of this function. In order to instantiate T +// in the example above (which could for example be the first type parameter of +// the class of the caller), the code at run time extracts the type arguments of +// the receiver at an offset in the receiver specified by the provided +// instantiator_class. +// +// If the caller to the constructor or to the factory is a factory, then the +// instantiator is the first parameter of this factory, which is already a +// type argument vector. This case is identified by a null and unneeded +// instantiator_class. +class ConstructorCallNode : public AstNode { + public: + ConstructorCallNode(intptr_t token_index, + const TypeArguments& type_arguments, + const Function& constructor, + ArgumentListNode* arguments) + : AstNode(token_index), + type_arguments_(type_arguments), + constructor_(constructor), + arguments_(arguments) { + ASSERT(type_arguments_.IsZoneHandle()); + ASSERT(constructor_.IsZoneHandle()); + ASSERT(arguments_ != NULL); + ASSERT(!Class::Handle(constructor_.owner()).IsParameterized() || + type_arguments_.IsNull() || + (type_arguments_.Length() == + Class::Handle(constructor_.owner()).NumTypeArguments())); + } + + const TypeArguments& type_arguments() const { return type_arguments_; } + const Function& constructor() const { return constructor_; } + ArgumentListNode* arguments() const { return arguments_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + arguments()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(ConstructorCallNode); + + private: + const TypeArguments& type_arguments_; + const Function& constructor_; + ArgumentListNode* arguments_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ConstructorCallNode); +}; + + +// The body of a Dart function marked as 'native' consists of this node. +class NativeBodyNode : public AstNode { + public: + NativeBodyNode(intptr_t token_index, + const String& native_c_function_name, + NativeFunction native_c_function, + int argument_count, + bool has_optional_parameters) + : AstNode(token_index), + native_c_function_name_(native_c_function_name), + native_c_function_(native_c_function), + argument_count_(argument_count), + has_optional_parameters_(has_optional_parameters) { + ASSERT(native_c_function_ != NULL); + ASSERT(native_c_function_name_.IsZoneHandle()); + ASSERT(native_c_function_name_.IsSymbol()); + } + + const String& native_c_function_name() const { + return native_c_function_name_; + } + NativeFunction native_c_function() const { return native_c_function_; } + int argument_count() const { return argument_count_; } + bool has_optional_parameters() const { + return has_optional_parameters_; + } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { } + + DECLARE_COMMON_NODE_FUNCTIONS(NativeBodyNode); + + private: + const String& native_c_function_name_; + NativeFunction native_c_function_; // Actual non-Dart implementation. + const int argument_count_; // Native Dart function argument count. + const bool has_optional_parameters_; // Native Dart function kind. + + DISALLOW_IMPLICIT_CONSTRUCTORS(NativeBodyNode); +}; + + +class CatchClauseNode : public AstNode { + public: + static const int kInvalidTryIndex = -1; + + CatchClauseNode(intptr_t token_index, + SequenceNode* catch_block, + const LocalVariable& context_var, + const LocalVariable& exception_var, + const LocalVariable& stacktrace_var) + : AstNode(token_index), + try_index_(kInvalidTryIndex), + catch_block_(catch_block), + context_var_(context_var), + exception_var_(exception_var), + stacktrace_var_(stacktrace_var) { + ASSERT(catch_block != NULL); + } + + int try_index() const { + ASSERT(try_index_ >= 0); + return try_index_; + } + void set_try_index(int value) { try_index_ = value; } + + const LocalVariable& context_var() const { return context_var_; } + const LocalVariable& exception_var() const { return exception_var_; } + const LocalVariable& stacktrace_var() const { return stacktrace_var_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + catch_block_->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(CatchClauseNode); + + private: + int try_index_; // Running index of the try blocks seen in a function. + SequenceNode* catch_block_; + const LocalVariable& context_var_; + const LocalVariable& exception_var_; + const LocalVariable& stacktrace_var_; + + DISALLOW_COPY_AND_ASSIGN(CatchClauseNode); +}; + + +class TryCatchNode : public AstNode { + public: + TryCatchNode(intptr_t token_index, + SequenceNode* try_block, + SourceLabel* end_catch_label, + const LocalVariable& context_var, + CatchClauseNode* catch_block, + SequenceNode* finally_block) + : AstNode(token_index), + try_block_(try_block), + end_catch_label_(end_catch_label), + context_var_(context_var), + catch_block_(catch_block), + finally_block_(finally_block) { + ASSERT(try_block != NULL); + ASSERT(catch_block != NULL || finally_block != NULL); + ASSERT(end_catch_label != NULL); + } + + SequenceNode* try_block() const { return try_block_; } + SourceLabel* end_catch_label() const { return end_catch_label_; } + CatchClauseNode* catch_block() const { return catch_block_; } + SequenceNode* finally_block() const { return finally_block_; } + const LocalVariable& context_var() const { return context_var_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + try_block_->Visit(visitor); + if (catch_block_ != NULL) { + catch_block_->Visit(visitor); + } + if (finally_block_ != NULL) { + finally_block_->Visit(visitor); + } + } + + DECLARE_COMMON_NODE_FUNCTIONS(TryCatchNode); + + private: + SequenceNode* try_block_; + SourceLabel* end_catch_label_; + const LocalVariable& context_var_; + CatchClauseNode* catch_block_; + SequenceNode* finally_block_; + + DISALLOW_COPY_AND_ASSIGN(TryCatchNode); +}; + + +class ThrowNode : public AstNode { + public: + ThrowNode(intptr_t token_index, AstNode* exception, AstNode* stacktrace) + : AstNode(token_index), exception_(exception), stacktrace_(stacktrace) { + ASSERT(exception != NULL); + } + + AstNode* exception() const { return exception_; } + AstNode* stacktrace() const { return stacktrace_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + exception()->Visit(visitor); + if (stacktrace() != NULL) { + stacktrace()->Visit(visitor); + } + } + + DECLARE_COMMON_NODE_FUNCTIONS(ThrowNode); + private: + AstNode* exception_; + AstNode* stacktrace_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ThrowNode); +}; + + +class InlinedFinallyNode : public AstNode { + public: + InlinedFinallyNode(intptr_t token_index, + AstNode* finally_block, + const LocalVariable& context_var) + : AstNode(token_index), + finally_block_(finally_block), + context_var_(context_var) { + ASSERT(finally_block != NULL); + } + + AstNode* finally_block() const { return finally_block_; } + const LocalVariable& context_var() const { return context_var_; } + + virtual void VisitChildren(AstNodeVisitor* visitor) const { + finally_block()->Visit(visitor); + } + + DECLARE_COMMON_NODE_FUNCTIONS(InlinedFinallyNode); + private: + AstNode* finally_block_; + const LocalVariable& context_var_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(InlinedFinallyNode); +}; + +} // namespace dart + +#undef DECLARE_COMMON_NODE_FUNCTIONS + +#endif // VM_AST_H_ diff --git a/runtime/vm/ast_printer.cc b/runtime/vm/ast_printer.cc new file mode 100644 index 00000000000..f3ccb836991 --- /dev/null +++ b/runtime/vm/ast_printer.cc @@ -0,0 +1,485 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/ast_printer.h" + +#include "vm/handles.h" +#include "vm/object.h" +#include "vm/os.h" +#include "vm/parser.h" + +namespace dart { + +AstPrinter::AstPrinter() { } + + +AstPrinter::~AstPrinter() { } + + +void AstPrinter::VisitGenericAstNode(AstNode* node) { + OS::Print("(%d: %s ", node->id(), node->Name()); + node->VisitChildren(this); + OS::Print(")"); +} + + +void AstPrinter::VisitSequenceNode(SequenceNode* node_sequence) { + // TODO(regis): Make the output more readable by indenting the nested + // sequences. This could be achieved using a AstPrinterContext similar to the + // CodeGeneratorContext. + ASSERT(node_sequence != NULL); + for (int i = 0; i < node_sequence->length(); i++) { + OS::Print("id %d: ", node_sequence->NodeAt(i)->id()); + node_sequence->NodeAt(i)->Visit(this); + OS::Print("\n"); + } +} + + +void AstPrinter::VisitArgumentListNode(ArgumentListNode* arguments) { + VisitGenericAstNode(arguments); +} + + +void AstPrinter::VisitReturnNode(ReturnNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitGenericLocalNode(AstNode* node, + const LocalVariable& var) { + OS::Print("(%s %s%s '%s'", + node->Name(), + var.is_final() ? "final " : "", + var.type().ToCString(), + var.name().ToCString()); + if (var.HasIndex()) { + OS::Print(" @%d", var.index()); + if (var.is_captured()) { + OS::Print(" ctx %d", var.owner()->context_level()); + } + } + node->VisitChildren(this); + OS::Print(")"); +} + + +void AstPrinter::VisitLoadLocalNode(LoadLocalNode* node) { + VisitGenericLocalNode(node, node->local()); +} + + +void AstPrinter::VisitStoreLocalNode(StoreLocalNode* node) { + VisitGenericLocalNode(node, node->local()); +} + + +void AstPrinter::VisitGenericFieldNode(AstNode* node, const Field& field) { + OS::Print("(%s %s%s '%s' ", + node->Name(), + field.is_final() ? "final " : "", + Type::Handle(field.type()).ToCString(), + String::Handle(field.name()).ToCString()); + node->VisitChildren(this); + OS::Print(")"); +} + + +void AstPrinter::VisitLoadInstanceFieldNode(LoadInstanceFieldNode* node) { + VisitGenericFieldNode(node, node->field()); +} + + +void AstPrinter::VisitStoreInstanceFieldNode(StoreInstanceFieldNode* node) { + VisitGenericFieldNode(node, node->field()); +} + + +void AstPrinter::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) { + VisitGenericFieldNode(node, node->field()); +} + + +void AstPrinter::VisitStoreStaticFieldNode(StoreStaticFieldNode* node) { + VisitGenericFieldNode(node, node->field()); +} + + +void AstPrinter::VisitArrayNode(ArrayNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitLiteralNode(LiteralNode* node) { + const Instance& literal = node->literal(); + OS::Print("'%s'", literal.ToCString()); +} + + +void AstPrinter::VisitTypeNode(TypeNode* node) { + const Type& type = node->type(); + OS::Print("'%s'", type.ToCString()); +} + + +void AstPrinter::VisitPrimaryNode(PrimaryNode* node) { + OS::Print("***** PRIMARY NODE IN AST ***** (%s '%s')", + node->Name(), node->primary().ToCString()); +} + + +void AstPrinter::VisitComparisonNode(ComparisonNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitBinaryOpNode(BinaryOpNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitStringConcatNode(StringConcatNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitUnaryOpNode(UnaryOpNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitConditionalExprNode(ConditionalExprNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitIfNode(IfNode* node) { + OS::Print("(if "); + node->condition()->Visit(this); + OS::Print(" then "); + node->true_branch()->Visit(this); + OS::Print(" else "); + if (node->false_branch() != NULL) { + node->false_branch()->Visit(this); + } + OS::Print(")"); +} + + +void AstPrinter::VisitCaseNode(CaseNode* node) { + OS::Print("(case ("); + for (int i = 0; i < node->case_expressions()->length(); i++) { + node->case_expressions()->NodeAt(i)->Visit(this); + } + if (node->contains_default()) { + OS::Print(" default "); + } + OS::Print(")"); + node->statements()->Visit(this); + OS::Print(")"); +} + + +void AstPrinter::VisitSwitchNode(SwitchNode *node) { + OS::Print("(switch "); + node->body()->Visit(this); + OS::Print(")"); +} + + +void AstPrinter::VisitWhileNode(WhileNode* node) { + OS::Print("(while "); + node->condition()->Visit(this); + OS::Print(" do "); + node->body()->Visit(this); + OS::Print(")"); +} + + +void AstPrinter::VisitForNode(ForNode* node) { + OS::Print("(for (init: "); + node->initializer()->Visit(this); + OS::Print("; cond:"); + if (node->condition() != NULL) { + node->condition()->Visit(this); + } + OS::Print("; incr:"); + node->increment()->Visit(this); + OS::Print(") body:"); + node->body()->Visit(this); + OS::Print("endfor)"); +} + + +void AstPrinter::VisitDoWhileNode(DoWhileNode* node) { + OS::Print("(do "); + node->body()->Visit(this); + OS::Print(" while "); + node->condition()->Visit(this); + OS::Print(")"); +} + + +void AstPrinter::VisitJumpNode(JumpNode* node) { + OS::Print("(%s %s)", node->Name(), node->label()->name().ToCString()); +} + + +void AstPrinter::VisitIncrOpLocalNode(IncrOpLocalNode* node) { + VisitGenericLocalNode(node, node->local()); +} + + +void AstPrinter::VisitIncrOpInstanceFieldNode(IncrOpInstanceFieldNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitIncrOpStaticFieldNode(IncrOpStaticFieldNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitIncrOpIndexedNode(IncrOpIndexedNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitInstanceCallNode(InstanceCallNode* node) { + OS::Print("(%s '%s'(", node->Name(), node->function_name().ToCString()); + node->VisitChildren(this); + OS::Print("))"); +} + + +void AstPrinter::VisitStaticCallNode(StaticCallNode* node) { + const char* function_fullname = node->function().ToFullyQualifiedCString(); + OS::Print("(%s '%s'(", node->Name(), function_fullname); + node->VisitChildren(this); + OS::Print("))"); +} + + +void AstPrinter::VisitClosureNode(ClosureNode* node) { + const char* function_fullname = node->function().ToFullyQualifiedCString(); + OS::Print("(%s '%s')", node->Name(), function_fullname); +} + + +void AstPrinter::VisitStaticImplicitClosureNode( + StaticImplicitClosureNode* node) { + const char* function_fullname = node->function().ToFullyQualifiedCString(); + OS::Print("static (%s '%s')", node->Name(), function_fullname); +} + + +void AstPrinter::VisitImplicitClosureNode(ImplicitClosureNode* node) { + const char* function_fullname = node->function().ToFullyQualifiedCString(); + OS::Print("(%s '%s')", node->Name(), function_fullname); +} + + +void AstPrinter::VisitClosureCallNode(ClosureCallNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitConstructorCallNode(ConstructorCallNode* node) { + const char* kind = node->constructor().IsFactory() ? "factory " : ""; + const char* constructor_name = node->constructor().ToFullyQualifiedCString(); + OS::Print("(%s %s'%s' ((this)", node->Name(), kind, constructor_name); + node->VisitChildren(this); + OS::Print("))"); +} + + +void AstPrinter::VisitInstanceGetterNode(InstanceGetterNode* node) { + OS::Print("(%s 'get %s'(", node->Name(), node->field_name().ToCString()); + node->VisitChildren(this); + OS::Print("))"); +} + + +void AstPrinter::VisitInstanceSetterNode(InstanceSetterNode* node) { + OS::Print("(%s 'set %s'(", node->Name(), node->field_name().ToCString()); + node->VisitChildren(this); + OS::Print("))"); +} + + +void AstPrinter::VisitStaticGetterNode(StaticGetterNode* node) { + String& class_name = String::Handle(node->cls().Name()); + OS::Print("(%s '%s.%s'(", + node->Name(), + class_name.ToCString(), + node->field_name().ToCString()); + OS::Print("))"); +} + + +void AstPrinter::VisitStaticSetterNode(StaticSetterNode* node) { + String& class_name = String::Handle(node->cls().Name()); + OS::Print("(%s '%s.%s'(", + node->Name(), class_name.ToCString(), node->field_name().ToCString()); + node->VisitChildren(this); + OS::Print("))"); +} + + +void AstPrinter::VisitLoadIndexedNode(LoadIndexedNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitStoreIndexedNode(StoreIndexedNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitNativeBodyNode(NativeBodyNode* node) { + OS::Print("(native_c call '%s'(%d args))", + node->native_c_function_name().ToCString(), + node->argument_count()); +} + + +void AstPrinter::VisitCatchClauseNode(CatchClauseNode* node) { + node->VisitChildren(this); +} + + +void AstPrinter::VisitTryCatchNode(TryCatchNode* node) { + OS::Print("("); + + // First visit the try block. + OS::Print("(try block ("); + node->try_block()->Visit(this); + OS::Print("))"); + + // Now visit the catch block if it exists. + if (node->catch_block() != NULL) { + OS::Print("(catch block () ("); + node->catch_block()->Visit(this); + OS::Print("))"); + } + + // Now visit the finally block if it exists. + if (node->finally_block() != NULL) { + OS::Print("(finally block () ("); + node->finally_block()->Visit(this); + OS::Print("))"); + } + + OS::Print(")"); +} + + +void AstPrinter::VisitThrowNode(ThrowNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::VisitInlinedFinallyNode(InlinedFinallyNode* node) { + VisitGenericAstNode(node); +} + + +void AstPrinter::PrintNode(AstNode* node) { + ASSERT(node != NULL); + AstPrinter ast_printer; + node->Visit(&ast_printer); + OS::Print("\n"); +} + + +void AstPrinter::PrintLocalScope(const LocalScope* scope, + int start_index) { + ASSERT(scope != NULL); + for (int i = start_index; i < scope->num_variables(); i++) { + LocalVariable* var = scope->VariableAt(i); + OS::Print("(%s%s '%s'", + var->is_final() ? "final " : "", + var->type().ToCString(), + var->name().ToCString()); + if (var->owner() != scope) { + OS::Print(" alias"); + } + if (var->HasIndex()) { + OS::Print(" @%d", var->index()); + if (var->is_captured()) { + OS::Print(" ctx %d", var->owner()->context_level()); + } + } else if (var->owner()->function_level() != 0) { + OS::Print(" lev %d", var->owner()->function_level()); + } + OS::Print(")"); + } + const LocalScope* child = scope->child(); + while (child != NULL) { + OS::Print("{scope "); + PrintLocalScope(child, 0); + OS::Print("}"); + child = child->sibling(); + } +} + + +void AstPrinter::PrintFunctionScope(const ParsedFunction& parsed_function) { + HANDLESCOPE(); + const Function& function = parsed_function.function(); + const Array& default_parameter_values = + parsed_function.default_parameter_values(); + SequenceNode* node_sequence = parsed_function.node_sequence(); + ASSERT(node_sequence != NULL); + const LocalScope* scope = node_sequence->scope(); + ASSERT(scope != NULL); + const char* function_name = function.ToFullyQualifiedCString(); + OS::Print("Scope for function '%s' {\n", function_name); + const int num_fixed_params = function.num_fixed_parameters(); + const int num_opt_params = function.num_optional_parameters(); + const int num_params = num_fixed_params + num_opt_params; + // Parameters must be listed first and must all appear in the top scope. + ASSERT(num_params <= scope->num_variables()); + int pos = 0; // Current position of variable in scope. + while (pos < num_params) { + LocalVariable* param = scope->VariableAt(pos); + ASSERT(param->owner() == scope); + OS::Print("(param %s%s '%s'", + param->is_final() ? "final " : "", + param->type().ToCString(), + param->name().ToCString()); + // Print the default value if the parameter is optional. + if (pos >= num_fixed_params && pos < num_params) { + const Object& default_parameter_value = Object::Handle( + default_parameter_values.At(pos - num_fixed_params)); + OS::Print(" =%s", default_parameter_value.ToCString()); + } + if (param->HasIndex()) { + OS::Print(" @%d", param->index()); + if (param->is_captured()) { + OS::Print(" ctx %d", param->owner()->context_level()); + } + } + OS::Print(")"); + pos++; + } + // Visit remaining non-parameter variables and children scopes. + PrintLocalScope(scope, pos); + OS::Print("}\n"); +} + + +void AstPrinter::PrintFunctionNodes(const ParsedFunction& parsed_function) { + HANDLESCOPE(); + SequenceNode* node_sequence = parsed_function.node_sequence(); + ASSERT(node_sequence != NULL); + AstPrinter ast_printer; + const char* function_name = + parsed_function.function().ToFullyQualifiedCString(); + OS::Print("Ast for function '%s' {\n", function_name); + node_sequence->Visit(&ast_printer); + OS::Print("}\n"); +} + +} // namespace dart diff --git a/runtime/vm/ast_printer.h b/runtime/vm/ast_printer.h new file mode 100644 index 00000000000..d61a802be62 --- /dev/null +++ b/runtime/vm/ast_printer.h @@ -0,0 +1,42 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_AST_PRINTER_H_ +#define VM_AST_PRINTER_H_ + +#include "vm/ast.h" +#include "vm/growable_array.h" + +namespace dart { + +// Forward declaration. +class ParsedFunction; + +class AstPrinter : public AstNodeVisitor { + public: + static void PrintNode(AstNode* node); + static void PrintFunctionScope(const ParsedFunction& parsed_function); + static void PrintFunctionNodes(const ParsedFunction& parsed_function); + static void PrintLocalScope(const LocalScope* scope, int variable_index); + + +#define DEFINE_VISITOR_FUNCTION(type, name) \ + virtual void Visit##type(type* node); +NODE_LIST(DEFINE_VISITOR_FUNCTION) +#undef DEFINE_VISITOR_FUNCTION + + private: + AstPrinter(); + ~AstPrinter(); + + void VisitGenericAstNode(AstNode* node); + void VisitGenericLocalNode(AstNode* node, const LocalVariable& local); + void VisitGenericFieldNode(AstNode* node, const Field& field); + + DISALLOW_COPY_AND_ASSIGN(AstPrinter); +}; + +} // namespace dart + +#endif // VM_AST_PRINTER_H_ diff --git a/runtime/vm/ast_printer_test.cc b/runtime/vm/ast_printer_test.cc new file mode 100644 index 00000000000..ad1cf1c5347 --- /dev/null +++ b/runtime/vm/ast_printer_test.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/ast_printer.h" +#include "vm/heap.h" +#include "vm/isolate.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/unit_test.h" + +namespace dart { + +TEST_CASE(AstPrinter) { + const intptr_t kPos = 1; // Dummy token index in non-existing source. + LocalVariable* v = + new LocalVariable(kPos, + String::ZoneHandle(String::New("wurscht")), + Type::ZoneHandle(Type::VarType())); + v->set_index(5); + LoadLocalNode* ll = new LoadLocalNode(kPos, *v); + ReturnNode* r = new ReturnNode(kPos, ll); + AstPrinter::PrintNode(r); + + AstNode* l = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3))); + ReturnNode* rl = new ReturnNode(kPos, l); + AstPrinter::PrintNode(rl); + + AstPrinter::PrintNode(new ReturnNode(kPos)); + + AstPrinter::PrintNode(new BinaryOpNode(kPos, + Token::kADD, + new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3))), + new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(5))))); + AstPrinter::PrintNode(new UnaryOpNode(kPos, Token::kSUB, ll)); +} + +} // namespace dart diff --git a/runtime/vm/ast_test.cc b/runtime/vm/ast_test.cc new file mode 100644 index 00000000000..064130a575f --- /dev/null +++ b/runtime/vm/ast_test.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/ast.h" +#include "vm/heap.h" +#include "vm/isolate.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/unit_test.h" + +namespace dart { + +static const intptr_t kPos = 1; // Dummy token index in non-existing source. + + +TEST_CASE(Ast) { + const intptr_t kPos = 1; // Dummy token index in non-existing source. + LocalVariable* v = new LocalVariable(kPos, + String::ZoneHandle(String::New("v")), + Type::ZoneHandle(Type::VarType())); + AstNode* ll = new LoadLocalNode(kPos, *v); + EXPECT(ll->IsLoadLocalNode()); + EXPECT(!ll->IsLiteralNode()); + LoadLocalNode* lln = ll->AsLoadLocalNode(); + EXPECT(NULL != lln); + v->set_index(1); + EXPECT_EQ(1, v->index()); + + LocalVariable* p = new LocalVariable(kPos, + String::ZoneHandle(String::New("p")), + Type::ZoneHandle(Type::VarType())); + EXPECT(!p->HasIndex()); + p->set_index(-1); + EXPECT(p->HasIndex()); + EXPECT_EQ(-1, p->index()); + + ReturnNode* r = new ReturnNode(kPos, lln); + EXPECT_EQ(lln, r->value()); + + LiteralNode* l = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3))); + EXPECT(l->literal().IsSmi()); + EXPECT_EQ(Smi::New(3), l->literal().raw()); + + BinaryOpNode* b = new BinaryOpNode(kPos, Token::kADD, l, lln); + EXPECT_EQ(Token::kADD, b->kind()); + EXPECT_EQ(l, b->left()); + EXPECT_EQ(lln, b->right()); + + UnaryOpNode* u = new UnaryOpNode(kPos, Token::kSUB, b); + EXPECT_EQ(Token::kSUB, u->kind()); + EXPECT_EQ(b, u->operand()); + + SequenceNode* sequence_node = new SequenceNode(1, new LocalScope(NULL, 0, 0)); + LiteralNode* literal_node = new LiteralNode(2, Smi::ZoneHandle(Smi::New(3))); + ReturnNode* return_node = new ReturnNode(3, literal_node); + sequence_node->Add(return_node); + GrowableArray nodes; + sequence_node->CollectAllNodes(&nodes); + EXPECT_EQ(3, nodes.length()); +} + +} // namespace dart diff --git a/runtime/vm/bigint_operations.cc b/runtime/vm/bigint_operations.cc new file mode 100644 index 00000000000..30e5ffd1c08 --- /dev/null +++ b/runtime/vm/bigint_operations.cc @@ -0,0 +1,558 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bigint_operations.h" + +#include + +#include "vm/bigint_store.h" +#include "vm/double_internals.h" +#include "vm/exceptions.h" +#include "vm/utils.h" +#include "vm/zone.h" + +namespace dart { + +bool Bigint::IsZero() const { return BN_is_zero(BNAddr()); } +bool Bigint::IsNegative() const { return !!BNAddr()->neg; } + + +void Bigint::SetSign(bool is_negative) const { + BIGNUM* bn = MutableBNAddr(); + // Danger Will Robinson! Use of OpenSSL internals! + // FIXME(benl): can be changed to use BN_set_negative() on more + // recent OpenSSL releases (> 1.0.0). + if (!is_negative || BN_is_zero(bn)) { + bn->neg = 0; + } else { + bn->neg = 1; + } +} + + +BIGNUM* BigintOperations::TmpBN() { + BigintStore* store = BigintStore::Get(); + if (store->bn_ == NULL) { + store->bn_ = BN_new(); + } + return store->bn_; +} + + +BN_CTX* BigintOperations::TmpBNCtx() { + BigintStore* store = BigintStore::Get(); + if (store->bn_ctx_ == NULL) { + store->bn_ctx_ = BN_CTX_new(); + } + return store->bn_ctx_; +} + + +RawBigint* BigintOperations::NewFromSmi(const Smi& smi, Heap::Space space) { + intptr_t value = smi.Value(); + bool is_negative = value < 0; + + if (is_negative) { + value = -value; + } + + BN_set_word(TmpBN(), value); + + const Bigint& result = Bigint::Handle(Bigint::New(TmpBN(), space)); + result.SetSign(is_negative); + + return result.raw(); +} + + +RawBigint* BigintOperations::NewFromInt64(int64_t value, Heap::Space space) { + bool is_negative = value < 0; + + if (is_negative) { + value = -value; + } + + const int kNumBytes = sizeof(value); + unsigned char pch[kNumBytes]; + for (int i = kNumBytes - 1; i >= 0; i--) { + unsigned char c = value & 0xFF; + value >>=8; + pch[i] = c; + } + + BN_bin2bn(pch, kNumBytes, TmpBN()); + + const Bigint& result = Bigint::Handle(Bigint::New(TmpBN(), space)); + result.SetSign(is_negative); + + return result.raw(); +} + + +RawBigint* BigintOperations::NewFromCString(const char* str, + Heap::Space space) { + ASSERT(str != NULL); + if (str[0] == '\0') { + return NewFromInt64(0, space); + } + + // If the string starts with '-' recursively restart the whole operation + // without the character and then toggle the sign. + // This allows multiple leading '-' (which will cancel each other out), but + // we have added an assert, to make sure that the returned result of the + // recursive call is not negative. + // We don't catch leading '-'s for zero. Ex: "--0", or "---". + if (str[0] == '-') { + const Bigint& result = Bigint::Handle(NewFromCString(&str[1], space)); + if (!result.IsNull()) { + result.ToggleSign(); + // FIXME(benl): this will fail if there is more than one leading '-'. + ASSERT(result.IsZero() || result.IsNegative()); + } + return result.raw(); + } + + intptr_t str_length = strlen(str); + if ((str_length > 2) && + (str[0] == '0') && + ((str[1] == 'x') || (str[1] == 'X'))) { + const Bigint& result = Bigint::Handle(FromHexCString(&str[2], space)); + return result.raw(); + } else { + return FromDecimalCString(str, space); + } +} + + +RawBigint* BigintOperations::NewFromDouble(double d, Heap::Space space) { + if ((-1.0 < d) && (d < 1.0)) { + // Shortcut for small numbers. Also makes the right-shift below + // well specified. + Smi& zero = Smi::Handle(Smi::New(0)); + return NewFromSmi(zero, space); + } + DoubleInternals internals = DoubleInternals(d); + if (internals.IsSpecial()) { + GrowableArray exception_arguments; + exception_arguments.Add( + &Object::ZoneHandle(String::New("BigintOperations::NewFromDouble"))); + exception_arguments.Add(&Object::ZoneHandle(Double::New(d))); + Exceptions::ThrowByType(Exceptions::kInternalError, + exception_arguments); + } + uint64_t significand = internals.Significand(); + int exponent = internals.Exponent(); + int sign = internals.Sign(); + if (exponent <= 0) { + significand >>= -exponent; + exponent = 0; + } else if (exponent <= 10) { + // A double significand has at most 53 bits. The following shift will + // hence not overflow, and yield an integer of at most 63 bits. + significand <<= exponent; + exponent = 0; + } + // A significand has at most 63 bits (after the shift above). + // The cast to int64_t is hence safe. + const Bigint& result = + Bigint::Handle(NewFromInt64(static_cast(significand), space)); + result.SetSign(sign < 0); + if (exponent > 0) { + return ShiftLeft(result, exponent); + } else { + return result.raw(); + } +} + + +RawBigint* BigintOperations::FromHexCString(const char* hex_string, + Heap::Space space) { + BIGNUM *bn = TmpBN(); + BN_hex2bn(&bn, hex_string); + ASSERT(bn == TmpBN()); + const Bigint& result = Bigint::Handle(Bigint::New(TmpBN(), space)); + return result.raw(); +} + + +RawBigint* BigintOperations::FromDecimalCString(const char* str, + Heap::Space space) { + BIGNUM *bn = TmpBN(); + int len = BN_dec2bn(&bn, str); + if (len == 0) { + return Bigint::null(); + } + ASSERT(bn == TmpBN()); + const Bigint& result = Bigint::Handle(Bigint::New(TmpBN(), space)); + return result.raw(); +} + + +const char* BigintOperations::ToHexCString(const BIGNUM *bn, + uword (*allocator)(intptr_t size)) { + char* str = BN_bn2hex(bn); + char* to_free = str; + intptr_t neg = 0; + if (str[0] == '-') { + ++str; + neg = 1; + } + if (str[0] == '0' && str[1] != '\0') { + ++str; + } + intptr_t length = strlen(str) + 3 + neg; + char *result = reinterpret_cast(allocator(length)); + if (neg) { + result[0] = '-'; + result[1] = '0'; + result[2] = 'x'; + // memcpy would suffice + memmove(result + 3, str, length - 3); + } else { + result[0] = '0'; + result[1] = 'x'; + // memcpy would suffice + memmove(result + 2, str, length - 2); + } + OPENSSL_free(to_free); + return result; +} + + +const char* BigintOperations::ToHexCString(const Bigint& bigint, + uword (*allocator)(intptr_t size)) { + return ToHexCString(bigint.BNAddr(), allocator); +} + + +bool BigintOperations::FitsIntoSmi(const Bigint& bigint) { + const BIGNUM *bn = bigint.BNAddr(); + int bits = BN_num_bits(bn); + // Special case for kMinValue as the absolute value is 1 bit longer + // than anything else + if (bits == Smi::kBits + 1 && BN_abs_is_word(bn, -Smi::kMinValue) + && bigint.IsNegative()) { + return true; + } + // All other cases must have no more bits than the size of an Smi + if (bits > Smi::kBits) { + return false; + } + return true; +} + + +RawSmi* BigintOperations::ToSmi(const Bigint& bigint) { + ASSERT(FitsIntoSmi(bigint)); + unsigned char bytes[kBitsPerWord / kBitsPerByte]; + ASSERT(BN_num_bytes(bigint.BNAddr()) <= static_cast(sizeof bytes)); + int n = BN_bn2bin(bigint.BNAddr(), bytes); + ASSERT(n >= 0); + intptr_t value = 0; + ASSERT(n <= static_cast(sizeof value)); + for (int i = 0; i < n; ++i) { + value <<= 8; + value |= bytes[i]; + } + if (bigint.IsNegative()) { + value = -value; + } + return Smi::New(value); +} + + +RawDouble* BigintOperations::ToDouble(const Bigint& bigint) { + // TODO(floitsch/benl): This is a quick and dirty implementation to unblock + // other areas of the code. It does not handle all bit-twiddling correctly. + double value = 0.0; + for (int i = bigint.NumberOfBits() - 1; i >= 0; --i) { + value *= 2; + value += static_cast(bigint.Bit(i)); + } + if (bigint.IsNegative()) { + value = -value; + } + return Double::New(value); +} + + +bool BigintOperations::FitsIntoInt64(const Bigint& bigint) { + const BIGNUM *bn = bigint.BNAddr(); + int bits = BN_num_bits(bn); + if (bits <= 63) return true; + if (bits > 64) return false; + if (!bigint.IsNegative()) return false; + // Special case for negative values, since Int64 representation may lose + // one bit. + ASSERT(bigint.Bit(63) != 0); + for (int i = 0; i < 63; i++) { + // Verify that all 63 least significant bits are 0. + if (bigint.Bit(i) != 0) return false; + } + return true; +} + + +int64_t BigintOperations::ToInt64(const Bigint& bigint) { + ASSERT(FitsIntoInt64(bigint)); + unsigned char bytes[8]; + ASSERT(BN_num_bytes(bigint.BNAddr()) <= static_cast(sizeof bytes)); + int n = BN_bn2bin(bigint.BNAddr(), bytes); + ASSERT(n >= 0); + int64_t value = 0; + ASSERT(n <= static_cast(sizeof value)); + for (int i = 0; i < n; ++i) { + value <<= 8; + value |= bytes[i]; + } + if (bigint.IsNegative()) { + value = -value; + } + return value; +} + + +RawBigint* BigintOperations::Add(const Bigint& a, const Bigint& b) { + int status = BN_add(TmpBN(), a.BNAddr(), b.BNAddr()); + ASSERT(status == 1); + const Bigint& result = Bigint::Handle(Bigint::New(TmpBN())); + return result.raw(); +} + + +RawBigint* BigintOperations::Subtract(const Bigint& a, const Bigint& b) { + int status = BN_sub(TmpBN(), a.BNAddr(), b.BNAddr()); + ASSERT(status == 1); + const Bigint& result = Bigint::Handle(Bigint::New(TmpBN())); + return result.raw(); +} + + +RawBigint* BigintOperations::Multiply(const Bigint& a, const Bigint& b) { + int status = BN_mul(TmpBN(), a.BNAddr(), b.BNAddr(), TmpBNCtx()); + ASSERT(status == 1); + const Bigint& result = Bigint::Handle(Bigint::New(TmpBN())); + return result.raw(); +} + + +RawBigint* BigintOperations::Divide(const Bigint& a, const Bigint& b) { + int status = BN_div(TmpBN(), NULL, a.BNAddr(), b.BNAddr(), TmpBNCtx()); + ASSERT(status == 1); + const Bigint& quotient = Bigint::Handle(Bigint::New(TmpBN())); + return quotient.raw(); +} + + +RawBigint* BigintOperations::Modulo(const Bigint& a, const Bigint& b) { + int status = BN_nnmod(TmpBN(), a.BNAddr(), b.BNAddr(), TmpBNCtx()); + ASSERT(status == 1); + const Bigint& modulo = Bigint::Handle(Bigint::New(TmpBN())); + return modulo.raw(); +} + + +RawBigint* BigintOperations::Remainder(const Bigint& a, const Bigint& b) { + int status = BN_div(NULL, TmpBN(), a.BNAddr(), b.BNAddr(), TmpBNCtx()); + ASSERT(status == 1); + const Bigint& remainder = Bigint::Handle(Bigint::New(TmpBN())); + return remainder.raw(); +} + + +RawBigint* BigintOperations::ShiftLeft(const Bigint& bigint, intptr_t amount) { + int status = BN_lshift(TmpBN(), bigint.BNAddr(), amount); + ASSERT(status == 1); + const Bigint& result = Bigint::Handle(Bigint::New(TmpBN())); + return result.raw(); +} + + +RawBigint* BigintOperations::ShiftRight(const Bigint& bigint, intptr_t amount) { + ASSERT(amount >= 0); + int status = BN_rshift(TmpBN(), bigint.BNAddr(), amount); + ASSERT(status == 1); + + // OpenSSL doesn't take account of sign when shifting - this fixes it. + if (bigint.IsNegative()) { + for (intptr_t i = 0; i < amount; ++i) { + if (bigint.IsBitSet(i)) { + int status = BN_sub_word(TmpBN(), 1); + ASSERT(status == 1); + break; + } + } + } + const Bigint& result = Bigint::Handle(Bigint::New(TmpBN())); + return result.raw(); +} + +/* Bit operations are complicated by negatives: BIGNUMs don't use 2's + * complement, but instead store the absolute value and a sign + * indicator. However bit operations are defined on 2's complement + * representations. This function handles the necessary + * invert-and-carry operations to deal with the fact that -x = ~x + 1 + * both on the operands and result. The actual operation performed is + * specified by the truth table |tt|, which is in the order of input + * bits (00, 01, 10, 11). + */ +RawBigint* BigintOperations::BitTT(const Bigint& a, const Bigint& b, + bool tt[4]) { + BN_zero(TmpBN()); + int n; + int rflip = 0; + int rborrow = 0; + // There's probably a clever way to figure out why these are what + // they are, but I confess I worked them out pragmatically. + if (a.IsNegative() && b.IsNegative()) { + if (tt[3]) { + rflip = -1; + rborrow = -1; + } + if (tt[2] != tt[3]) { + n = Utils::Maximum(a.NumberOfBits(), b.NumberOfBits()); + } else { + n = Utils::Minimum(a.NumberOfBits(), b.NumberOfBits()); + } + } else if (a.IsNegative() || b.IsNegative()) { + if (tt[2]) { + rflip = rborrow = -1; + } + n = Utils::Maximum(a.NumberOfBits(), b.NumberOfBits()); + } else { + if (tt[2]) { + n = Utils::Maximum(a.NumberOfBits(), b.NumberOfBits()); + } else { + n = Utils::Minimum(a.NumberOfBits(), b.NumberOfBits()); + } + } + bool aflip = false; + int acarry = 0; + if (a.IsNegative()) { + aflip = true; + acarry = 1; + } + bool bflip = false; + int bcarry = 0; + if (b.IsNegative()) { + bflip = true; + bcarry = 1; + } + for (int i = 0 ; i < n ; ++i) { + int ab = (a.Bit(i) ^ (aflip ? 1 : 0)) + acarry; + ASSERT(ab <= 2 && ab >= 0); + int bb = (b.Bit(i) ^ (bflip ? 1 : 0)) + bcarry; + ASSERT(bb <= 2 && bb >= 0); + int r = tt[(ab & 1) + ((bb & 1) << 1)]; + r = r + rborrow; + ASSERT(r >= -1 && r <= 1); + if ((r ^ rflip) & 1) { + int status = BN_set_bit(TmpBN(), i); + ASSERT(status == 1); + } + acarry = ab >> 1; + bcarry = bb >> 1; + rborrow = r >> 1; + } + if (rborrow) { + int status = BN_set_bit(TmpBN(), n); + ASSERT(status == 1); + } + if (rflip) { + // FIXME(benl): can be changed to use BN_set_negative() on more + // recent OpenSSL releases (> 1.0.0). + ASSERT(!BN_is_zero(TmpBN())); + TmpBN()->neg = 1; + } + const Bigint& result = Bigint::Handle(Bigint::New(TmpBN())); + return result.raw(); +} + + +RawSmi* BigintOperations::BitOpWithSmi(Token::Kind kind, + const Bigint& bigint, + const Smi& smi) { + ASSERT((kind == Token::kBIT_OR) || (kind == Token::kBIT_AND)); + intptr_t smi_value = smi.Value(); + intptr_t big_value = 0; + // We take Smi::kBits + 1 (one more bit), in case bigint is negative. + ASSERT((Smi::kBits + 1) <= (sizeof(big_value) * kBitsPerByte)); + intptr_t num_bits = bigint.NumberOfBits(); + int n = Utils::Minimum(num_bits, Smi::kBits + 1); + for (int i = n - 1; i >= 0; i--) { + big_value <<= 1; + big_value += bigint.Bit(i); + } + if (bigint.IsNegative()) { + big_value = -big_value; + } + intptr_t result; + if (kind == Token::kBIT_OR) { + result = smi_value | big_value; + } else { + result = smi_value & big_value; + } + ASSERT(Smi::IsValid(result)); + return Smi::New(result); +} + + +RawBigint* BigintOperations::BitAnd(const Bigint& a, const Bigint& b) { + static bool tt_and[] = { false, false, false, true }; + return BitTT(a, b, tt_and); +} + + +RawInteger* BigintOperations::BitAndWithSmi(const Bigint& bigint, + const Smi& smi) { + if (smi.IsNegative()) { + Bigint& other = Bigint::Handle(NewFromSmi(smi)); + return BitAnd(bigint, other); + } + return BitOpWithSmi(Token::kBIT_AND, bigint, smi); +} + + +RawBigint* BigintOperations::BitOr(const Bigint& a, const Bigint& b) { + static bool tt_or[] = { false, true, true, true }; + return BitTT(a, b, tt_or); +} + + +RawInteger* BigintOperations::BitOrWithSmi(const Bigint& bigint, + const Smi& smi) { + if (!smi.IsNegative()) { + Bigint& other = Bigint::Handle(NewFromSmi(smi)); + return BitOr(bigint, other); + } + return BitOpWithSmi(Token::kBIT_OR, bigint, smi); +} + + +RawBigint* BigintOperations::BitXor(const Bigint& a, const Bigint& b) { + static bool tt_xor[] = { false, true, true, false }; + return BitTT(a, b, tt_xor); +} + + +RawInteger* BigintOperations::BitXorWithSmi(const Bigint& bigint, + const Smi& smi) { + Bigint& other = Bigint::Handle(NewFromSmi(smi)); + return BitXor(bigint, other); +} + + +RawBigint* BigintOperations::BitNot(const Bigint& bigint) { + const Bigint& one_bigint = Bigint::Handle(One()); + const Bigint& result = Bigint::Handle(Add(bigint, one_bigint)); + result.ToggleSign(); + return result.raw(); +} + + +int BigintOperations::Compare(const Bigint& a, const Bigint& b) { + return BN_cmp(a.BNAddr(), b.BNAddr()); +} + +} // namespace dart diff --git a/runtime/vm/bigint_operations.h b/runtime/vm/bigint_operations.h new file mode 100644 index 00000000000..d121d20fc13 --- /dev/null +++ b/runtime/vm/bigint_operations.h @@ -0,0 +1,94 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_BIGINT_OPERATIONS_H_ +#define VM_BIGINT_OPERATIONS_H_ + +#include "vm/heap.h" +#include "vm/object.h" +#include "vm/token.h" + +// This should be folded into OpenSSL +#undef BN_abs_is_word +#define BN_abs_is_word(a, w) (((a)->top == 1) \ + && ((a)->d[0] == static_cast(w))) + +namespace dart { + +class BigintOperations : public AllStatic { + public: + static RawBigint* NewFromSmi(const Smi& smi, Heap::Space space = Heap::kNew); + static RawBigint* NewFromInt64(int64_t value, Heap::Space space = Heap::kNew); + // The given string must be a valid integer representation. It may be + // prefixed by a minus and/or "0x". + // Returns Bigint::null() if the string cannot be parsed. + static RawBigint* NewFromCString(const char* str, + Heap::Space space = Heap::kNew); + static RawBigint* NewFromDouble(double d, Heap::Space space = Heap::kNew); + + // The given string must be a nul-terminated string of hex-digits. It must + // only contain hex-digits. No sign or leading "0x" is allowed. + static RawBigint* FromHexCString(const char* str, + Heap::Space space = Heap::kNew); + // The given string must be a nul-terminated string of decimal digits. It + // must only contain decimal digits (0-9). No sign is allowed. Leading + // zeroes are ignored. + static RawBigint* FromDecimalCString(const char* str, + Heap::Space space = Heap::kNew); + // Converts the bigint to a string. The returned string is prepended by + // a "0x" (after the optional minus-sign). + static const char* ToHexCString(const BIGNUM* bn, + uword (*allocator)(intptr_t size)); + static const char* ToHexCString(const Bigint& bigint, + uword (*allocator)(intptr_t size)); + + static bool FitsIntoSmi(const Bigint& bigint); + static RawSmi* ToSmi(const Bigint& bigint); + + static bool FitsIntoInt64(const Bigint& bigint); + static int64_t ToInt64(const Bigint& bigint); + + static RawDouble* ToDouble(const Bigint& bigint); + + static RawBigint* Add(const Bigint& a, const Bigint& b); + static RawBigint* Subtract(const Bigint& a, const Bigint& b); + static RawBigint* Multiply(const Bigint& a, const Bigint& b); + // TODO(floitsch): what to do for divisions by zero. + static RawBigint* Divide(const Bigint& a, const Bigint& b); + static RawBigint* Modulo(const Bigint& a, const Bigint& b); + static RawBigint* Remainder(const Bigint& a, const Bigint& b); + + static RawBigint* ShiftLeft(const Bigint& bigint, intptr_t amount); + static RawBigint* ShiftRight(const Bigint& bigint, intptr_t amount); + static RawBigint* BitAnd(const Bigint& a, const Bigint& b); + static RawBigint* BitOr(const Bigint& a, const Bigint& b); + static RawBigint* BitXor(const Bigint& a, const Bigint& b); + static RawBigint* BitNot(const Bigint& bigint); + static RawInteger* BitAndWithSmi(const Bigint& bigint, const Smi& smi); + static RawInteger* BitOrWithSmi(const Bigint& bigint, const Smi& smi); + static RawInteger* BitXorWithSmi(const Bigint& bigint, const Smi& smi); + + static int Compare(const Bigint& a, const Bigint& b); + + + private: + static BIGNUM* TmpBN(); + static BN_CTX* TmpBNCtx(); + + static RawBigint* One() { + Bigint& result = Bigint::Handle(NewFromInt64(1)); + return result.raw(); + } + static RawBigint* BitTT(const Bigint& a, const Bigint& b, bool tt[4]); + // The following function only works for bit-and and bit-or. + static RawSmi* BitOpWithSmi(Token::Kind kind, + const Bigint& bigint, + const Smi& smi); + + DISALLOW_IMPLICIT_CONSTRUCTORS(BigintOperations); +}; + +} // namespace dart + +#endif // VM_BIGINT_OPERATIONS_H_ diff --git a/runtime/vm/bigint_operations_test.cc b/runtime/vm/bigint_operations_test.cc new file mode 100644 index 00000000000..9e4468a09e8 --- /dev/null +++ b/runtime/vm/bigint_operations_test.cc @@ -0,0 +1,2009 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/bigint_operations.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/unit_test.h" + +namespace dart { + +static uword ZoneAllocator(intptr_t size) { + Zone* zone = Isolate::Current()->current_zone(); + return zone->Allocate(size); +} + + +TEST_CASE(BigintSmi) { + { + const Smi& smi = Smi::Handle(Smi::New(5)); + const Bigint& bigint = Bigint::Handle(BigintOperations::NewFromSmi(smi)); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi_back = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT_EQ(5, smi_back.Value()); + } + + { + const Smi& smi = Smi::Handle(Smi::New(Smi::kMaxValue)); + const Bigint& bigint = Bigint::Handle(BigintOperations::NewFromSmi(smi)); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi_back = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT(Smi::kMaxValue == smi_back.Value()); + } + + { + const Smi& smi = Smi::Handle(Smi::New(Smi::kMinValue)); + const Bigint& bigint = Bigint::Handle(BigintOperations::NewFromSmi(smi)); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi_back = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT(bigint.IsNegative()); + EXPECT(Smi::kMinValue == smi_back.Value()); + } + + { + ASSERT(0xFFFFFFF < Smi::kMaxValue); + const Smi& smi = Smi::Handle(Smi::New(0xFFFFFFF)); + const Bigint& bigint = Bigint::Handle(BigintOperations::NewFromSmi(smi)); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi_back = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT(0xFFFFFFF == smi_back.Value()); + } + + { + ASSERT(0x10000000 < Smi::kMaxValue); + const Smi& smi = Smi::Handle(Smi::New(0x10000000)); + const Bigint& bigint = Bigint::Handle(BigintOperations::NewFromSmi(smi)); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi_back = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT(0x10000000 == smi_back.Value()); + } +} + + +TEST_CASE(BigintInt64) { + const int64_t kValue = 100000000; + const int64_t kValue64 = kValue * kValue; + Bigint& big = Bigint::Handle(BigintOperations::NewFromInt64(kValue)); + const Smi& smi = Smi::Handle(Smi::New(kValue)); + Bigint& big_test = Bigint::Handle(BigintOperations::NewFromSmi(smi)); + EXPECT_EQ(0, BigintOperations::Compare(big, big_test)); + big = BigintOperations::NewFromInt64(kValue64); + big_test = BigintOperations::Multiply(big_test, big_test); + EXPECT_EQ(0, BigintOperations::Compare(big, big_test)); + big = BigintOperations::NewFromInt64(-kValue64); + big_test = BigintOperations::Subtract( + Bigint::Handle(BigintOperations::NewFromInt64(0)), big_test); + EXPECT_EQ(0, BigintOperations::Compare(big, big_test)); + + const Bigint& one = Bigint::Handle(BigintOperations::NewFromInt64(1)); + const int64_t kMaxValue64 = + static_cast(DART_2PART_UINT64_C(0x7FFFFFFF, FFFFFFFF)); + const int64_t kMinValue64 = + static_cast(DART_2PART_UINT64_C(0x80000000, 00000000)); + big = BigintOperations::NewFromInt64(kMinValue64); + EXPECT(BigintOperations::FitsIntoInt64(big)); + int64_t back = BigintOperations::ToInt64(big); + EXPECT_EQ(kMinValue64, back); + + big = BigintOperations::Subtract(big, one); + EXPECT(!BigintOperations::FitsIntoInt64(big)); + + big = BigintOperations::NewFromInt64(kMaxValue64); + EXPECT(BigintOperations::FitsIntoInt64(big)); + back = BigintOperations::ToInt64(big); + EXPECT_EQ(kMaxValue64, back); + + big = BigintOperations::Add(big, one); + EXPECT(!BigintOperations::FitsIntoInt64(big)); +} + + +TEST_CASE(BigintDouble) { + Smi& smi = Smi::Handle(Smi::New(5)); + Bigint& bigint = Bigint::Handle(BigintOperations::NewFromSmi(smi)); + Double& dbl = Double::Handle(BigintOperations::ToDouble(bigint)); + EXPECT_EQ(5.0, dbl.value()); + + smi = Smi::New(0); + bigint = BigintOperations::NewFromSmi(smi); + dbl = BigintOperations::ToDouble(bigint); + EXPECT_EQ(0.0, dbl.value()); + double zero = dbl.value(); + + smi = Smi::New(-12345678); + bigint = BigintOperations::NewFromSmi(smi); + dbl = BigintOperations::ToDouble(bigint); + EXPECT_EQ(-1.2345678e+7, dbl.value()); + + bigint = BigintOperations::NewFromCString("98765432109876"); + dbl = BigintOperations::ToDouble(bigint); + EXPECT_EQ(9.8765432109876e+13, dbl.value()); + + // Reduced precision. + bigint = BigintOperations::NewFromCString( + "9876543210987654321098765432109876543210"); + dbl = BigintOperations::ToDouble(bigint); + // TODO(floitsch): Proper rounding if deemed necessary. + EXPECT_EQ(9.8765432109876534e+39, dbl.value()); + + bigint = BigintOperations::NewFromCString( + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890"); + dbl = BigintOperations::ToDouble(bigint); + EXPECT_EQ(1.0/zero, dbl.value()); + + bigint = BigintOperations::NewFromCString("100000000000000000000000"); + dbl = BigintOperations::ToDouble(bigint); + EXPECT_EQ(1e+23, dbl.value()); + + bigint = BigintOperations::NewFromCString("100000000000000000000001"); + dbl = BigintOperations::ToDouble(bigint); + // TODO(floitsch): Proper rounding if deemed necessary. + // EXPECT_EQ(1.0000000000000001e+23, dbl.value()); + EXPECT_EQ(9.9999999999999992e+22, dbl.value()); + + // Same but shifted 64 bits to the left. + bigint = BigintOperations::NewFromCString( + "1844674407370955161600000000000000000000000"); + dbl = BigintOperations::ToDouble(bigint); + EXPECT_EQ(1.844674407370955e+42, dbl.value()); + + bigint = BigintOperations::NewFromCString( + "1844674407370955161600000000000000000000001"); + dbl = BigintOperations::ToDouble(bigint); + // TODO(floitsch): Proper rounding if deemed necessary. + // EXPECT_EQ(1.8446744073709553e+42, dbl.value()); + EXPECT_EQ(1.844674407370955e+42, dbl.value()); +} + + +TEST_CASE(BigintHexStrings) { + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("0x0")); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT_EQ(0, smi.Value()); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("0x1")); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT_EQ(1, smi.Value()); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("0x123")); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT_EQ(0x123, smi.Value()); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("0x123")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("0x123", str); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("0xaBcEf")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("0xABCEF", str); + } + + { + const char* in = "0x123456789"; + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString(in)); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ(in, str); + } + + { + const char* in = "0xFFFFFFF"; + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString(in)); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ(in, str); + } + + { + const char* in = "0x10000000"; + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString(in)); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ(in, str); + } + + { + const char* in = "0x123456789ABCDEF01234567890ABCDEF0123456789ABCDEF0"; + const Bigint& bigint = Bigint::Handle(BigintOperations::NewFromCString(in)); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ(in, str); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("-0x123")); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT_EQ(-0x123, smi.Value()); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("-0x123")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("-0x123", str); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("-0xaBcEf")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("-0xABCEF", str); + } + + { + const char* in = "-0x123456789"; + const Bigint& bigint = Bigint::Handle(BigintOperations::NewFromCString(in)); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ(in, str); + } + + { + const char* in = "-0x123456789ABCDEF01234567890ABCDEF0123456789ABCDEF0"; + const Bigint& bigint = Bigint::Handle(BigintOperations::NewFromCString(in)); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ(in, str); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("0x00000123")); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT_EQ(0x123, smi.Value()); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("0x000000123")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("0x123", str); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("0x0000aBcEf")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("0xABCEF", str); + } + + { + const char* in = "0x00000000000000000000000000000000000000000000123456789"; + const char* out = "0x123456789"; + const Bigint& bigint = Bigint::Handle(BigintOperations::NewFromCString(in)); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ(out, str); + } + + { + const char* in = "0x00000123456789ABCDEF01234567890ABCDEF0123456789ABCDEF0"; + const char* out = "0x123456789ABCDEF01234567890ABCDEF0123456789ABCDEF0"; + const Bigint& bigint = Bigint::Handle(BigintOperations::NewFromCString(in)); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ(out, str); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("-0x00000123")); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT_EQ(-0x123, smi.Value()); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("-0x00000123")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("-0x123", str); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("-0x000aBcEf")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("-0xABCEF", str); + } + + { + const char* in = "-0x00000000000000000000000000000000000000000000123456789"; + const char* out = "-0x123456789"; + const Bigint& bigint = Bigint::Handle(BigintOperations::NewFromCString(in)); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ(out, str); + } + + { + const char* in = "-0x0000123456789ABCDEF01234567890ABCDEF0123456789ABCDEF0"; + const char* out = "-0x123456789ABCDEF01234567890ABCDEF0123456789ABCDEF0"; + const Bigint& bigint = Bigint::Handle(BigintOperations::NewFromCString(in)); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ(out, str); + } + { + const char* test = "12345678901234567890"; + const char* out = "0xAB54A98CEB1F0AD2"; + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString(test)); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ(out, str); + } + { + const char* test = "-12345678901234567890"; + const char* out = "-0xAB54A98CEB1F0AD2"; + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString(test)); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ(out, str); + } +} + + +static void TestBigintCompare(const char* a, const char* b, int compare) { + const Bigint& bigint_a = Bigint::Handle(BigintOperations::NewFromCString(a)); + const Bigint& bigint_b = Bigint::Handle(BigintOperations::NewFromCString(b)); + int computed_compare = BigintOperations::Compare(bigint_a, bigint_b); + int inverted_compare = BigintOperations::Compare(bigint_b, bigint_a); + if (compare == 0) { + EXPECT(computed_compare == 0); + EXPECT(inverted_compare == 0); + } else if (compare < 0) { + ASSERT(computed_compare < 0); + EXPECT(computed_compare < 0); + EXPECT(inverted_compare > 0); + } else { + ASSERT(compare > 0); + EXPECT(computed_compare > 0); + EXPECT(inverted_compare < 0); + } +} + + +TEST_CASE(BigintCompare) { + TestBigintCompare("0x0", "0x0", 0); + TestBigintCompare("0x1", "0x1", 0); + TestBigintCompare("-0x1", "-0x1", 0); + TestBigintCompare("0x1234567", "0x1234567", 0); + TestBigintCompare("-0x1234567", "-0x1234567", 0); + TestBigintCompare("0x12345678", "0x12345678", 0); + TestBigintCompare("-0x12345678", "-0x12345678", 0); + TestBigintCompare("0x123456789ABCDEF0", "0x123456789ABCDEF0", 0); + TestBigintCompare("-0x123456789ABCDEF0", "-0x123456789ABCDEF0", 0); + TestBigintCompare("0x123456789ABCDEF01", "0x123456789ABCDEF01", 0); + TestBigintCompare("-0x123456789ABCDEF01", "-0x123456789ABCDEF01", 0); + TestBigintCompare("0x1", "0x0", 1); + TestBigintCompare("-0x1", "-0x2", 1); + TestBigintCompare("0x1234567", "0x1234566", 1); + TestBigintCompare("-0x1234567", "-0x1234568", 1); + TestBigintCompare("0x12345678", "0x12345677", 1); + TestBigintCompare("-0x12345678", "-0x12345679", 1); + TestBigintCompare("0x123456789ABCDEF1", "0x123456789ABCDEF0", 1); + TestBigintCompare("-0x123456789ABCDEF0", "-0x123456789ABCDEF1", 1); + TestBigintCompare("0x123456789ABCDEF02", "0x123456789ABCDEF01", 1); + TestBigintCompare("-0x123456789ABCDEF00", "-0x123456789ABCDEF01", 1); + TestBigintCompare("0x10000000", "0xFFFFFFF", 1); + TestBigintCompare("-0x10000000", "-0xFFFFFFF", -1); + TestBigintCompare("0x100000000", "0xFFFFFFFF", 1); + TestBigintCompare("-0x100000000", "-0xFFFFFFFF", -1); + TestBigintCompare("0x10000000000000000", "0xFFFFFFFFFFFFFFFF", 1); + TestBigintCompare("-0x10000000000000000", "-0xFFFFFFFFFFFFFFFF", -1); + TestBigintCompare("0x10000000000000000", "0x0", 1); + TestBigintCompare("-0x10000000000000000", "0x0", -1); + TestBigintCompare("-0x1234567", "0x1234566", -1); + TestBigintCompare("-0x1234567", "0x1234568", -1); + TestBigintCompare("-0x12345678", "0x12345677", -1); + TestBigintCompare("-0x12345678", "0x12345670", -1); + TestBigintCompare("-0x123456789ABCDEF1", "0x123456789ABCDEF0", -1); + TestBigintCompare("-0x123456789ABCDEF0", "0x123456789ABCDEF1", -1); + TestBigintCompare("-0x123456789ABCDEF02", "0x123456789ABCDEF01", -1); + TestBigintCompare("-0x123456789ABCDEF00", "0x123456789ABCDEF01", -1); + TestBigintCompare("-0x10000000", "0xFFFFFFF", -1); + TestBigintCompare("-0x10000000", "0xFFFFFFF", -1); + TestBigintCompare("-0x100000000", "0xFFFFFFFF", -1); + TestBigintCompare("-0x100000000", "0xFFFFFFFF", -1); + TestBigintCompare("-0x10000000000000000", "0xFFFFFFFFFFFFFFFF", -1); + TestBigintCompare("-0x10000000000000000", "0xFFFFFFFFFFFFFFFF", -1); + TestBigintCompare("-0x10000000000000000", "0x0", -1); + TestBigintCompare("-0x10000000000000000", "0x0", -1); +} + + +TEST_CASE(BigintDecimalStrings) { + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("0")); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT_EQ(0, smi.Value()); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("1")); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT_EQ(1, smi.Value()); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("703710")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("0xABCDE", str); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("11259375")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("0xABCDEF", str); + } + + { + const Bigint& bigint = + Bigint::Handle(BigintOperations::NewFromCString("1311768467463790320")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("0x123456789ABCDEF0", str); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("-0")); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT_EQ(0, smi.Value()); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("-1")); + EXPECT(BigintOperations::FitsIntoSmi(bigint)); + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint)); + EXPECT_EQ(-1, smi.Value()); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("-703710")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("-0xABCDE", str); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("-11259375")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("-0xABCDEF", str); + } + + { + const Bigint& bigint = Bigint::Handle( + BigintOperations::NewFromCString("-1311768467463790320")); + const char* str = BigintOperations::ToHexCString(bigint, &ZoneAllocator); + EXPECT_STREQ("-0x123456789ABCDEF0", str); + } +} + + +static void TestBigintAddSubtract(const char* a, + const char* b, + const char* sum) { + const Bigint& bigint_a = Bigint::Handle(BigintOperations::NewFromCString(a)); + const Bigint& bigint_b = Bigint::Handle(BigintOperations::NewFromCString(b)); + const Bigint& bigint_sum = + Bigint::Handle(BigintOperations::NewFromCString(sum)); + const Bigint& computed_sum = + Bigint::Handle(BigintOperations::Add(bigint_a, bigint_b)); + const Bigint& computed_difference1 = + Bigint::Handle(BigintOperations::Subtract(bigint_sum, bigint_a)); + const Bigint& computed_difference2 = + Bigint::Handle(BigintOperations::Subtract(bigint_sum, bigint_b)); + const char* str_sum = BigintOperations::ToHexCString(computed_sum, + &ZoneAllocator); + EXPECT_STREQ(sum, str_sum); + const char* str_difference1 = + BigintOperations::ToHexCString(computed_difference1, &ZoneAllocator); + EXPECT_STREQ(b, str_difference1); + const char* str_difference2 = + BigintOperations::ToHexCString(computed_difference2, &ZoneAllocator); + EXPECT_STREQ(a, str_difference2); +} + + +TEST_CASE(BigintAddSubtract) { + const char* zero = "0x0"; + const char* one = "0x1"; + const char* minus_one = "-0x1"; + + TestBigintAddSubtract(zero, zero, zero); + TestBigintAddSubtract(zero, one, one); + TestBigintAddSubtract(one, zero, one); + TestBigintAddSubtract(one, one, "0x2"); + TestBigintAddSubtract(minus_one, minus_one, "-0x2"); + TestBigintAddSubtract("0x123", zero, "0x123"); + TestBigintAddSubtract(zero, "0x123", "0x123"); + TestBigintAddSubtract("0x123", one, "0x124"); + TestBigintAddSubtract(one, "0x123", "0x124"); + TestBigintAddSubtract("0xFFFFFFF", one, // 28 bit overflow. + "0x10000000"); + TestBigintAddSubtract("0xFFFFFFFF", one, // 32 bit overflow. + "0x100000000"); + TestBigintAddSubtract("0xFFFFFFFFFFFFFF", one, // 56 bit overflow. + "0x100000000000000"); + TestBigintAddSubtract("0xFFFFFFFFFFFFFFFF", one, // 64 bit overflow. + "0x10000000000000000"); + TestBigintAddSubtract("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // 128 bit. + one, + "0x100000000000000000000000000000000"); + TestBigintAddSubtract("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + one, + "0x10000000000000000000000000000000000000000000"); + TestBigintAddSubtract("0x8000000", // 28 bit overflow. + "0x8000000", + "0x10000000"); + TestBigintAddSubtract("0x80000000", // 32 bit overflow. + "0x80000000", + "0x100000000"); + TestBigintAddSubtract("0x80000000000000", // 56 bit overflow. + "0x80000000000000", + "0x100000000000000"); + TestBigintAddSubtract("0x8000000000000000", // 64 bit overflow. + "0x8000000000000000", + "0x10000000000000000"); + TestBigintAddSubtract("0x80000000000000000000000000000000", // 128 bit. + "0x80000000000000000000000000000000", + "0x100000000000000000000000000000000"); + TestBigintAddSubtract("0x8000000000000000000000000000000000000000000", + "0x8000000000000000000000000000000000000000000", + "0x10000000000000000000000000000000000000000000"); + + { + const char* a = "0x123456789ABCDEF01234567890ABCDEF0123456789ABCDEF0"; + const char* sum1 = "0x123456789ABCDEF01234567890ABCDEF0123456789ABCDEF1"; + const char* times2 = "0x2468ACF13579BDE02468ACF121579BDE02468ACF13579BDE0"; + TestBigintAddSubtract(a, zero, a); + TestBigintAddSubtract(a, one, sum1); + TestBigintAddSubtract(a, a, times2); + } + + TestBigintAddSubtract("-0x123", minus_one, "-0x124"); + TestBigintAddSubtract(minus_one, "-0x123", "-0x124"); + TestBigintAddSubtract("-0xFFFFFFF", minus_one, // 28 bit overflow. + "-0x10000000"); + TestBigintAddSubtract("-0xFFFFFFFF", minus_one, // 32 bit overflow. + "-0x100000000"); + TestBigintAddSubtract("-0xFFFFFFFFFFFFFF", minus_one, // 56 bit overflow. + "-0x100000000000000"); + TestBigintAddSubtract("-0xFFFFFFFFFFFFFFFF", minus_one, // 64 bit overflow. + "-0x10000000000000000"); + TestBigintAddSubtract("-0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // 128 bit. + minus_one, + "-0x100000000000000000000000000000000"); + TestBigintAddSubtract("-0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + minus_one, + "-0x10000000000000000000000000000000000000000000"); + TestBigintAddSubtract("-0x8000000", // 28 bit overflow. + "-0x8000000", + "-0x10000000"); + TestBigintAddSubtract("-0x80000000", // 32 bit overflow. + "-0x80000000", + "-0x100000000"); + TestBigintAddSubtract("-0x80000000000000", // 56 bit overflow. + "-0x80000000000000", + "-0x100000000000000"); + TestBigintAddSubtract("-0x8000000000000000", // 64 bit overflow. + "-0x8000000000000000", + "-0x10000000000000000"); + TestBigintAddSubtract("-0x80000000000000000000000000000000", // 128 bit. + "-0x80000000000000000000000000000000", + "-0x100000000000000000000000000000000"); + TestBigintAddSubtract("-0x8000000000000000000000000000000000000000000", + "-0x8000000000000000000000000000000000000000000", + "-0x10000000000000000000000000000000000000000000"); + + { + const char* a = "-0x123456789ABCDEF01234567890ABCDEF0123456789ABCDEF0"; + const char* sum1 = "-0x123456789ABCDEF01234567890ABCDEF0123456789ABCDEF1"; + const char* times2 = "-0x2468ACF13579BDE02468ACF121579BDE02468ACF13579BDE0"; + TestBigintAddSubtract(a, zero, a); + TestBigintAddSubtract(a, minus_one, sum1); + TestBigintAddSubtract(a, a, times2); + } + + TestBigintAddSubtract("0x10000000000000000000000000000000000000000000", + "0xFFFF", + "0x1000000000000000000000000000000000000000FFFF"); + TestBigintAddSubtract("0x10000000000000000000000000000000000000000000", + "0xFFFF00000000", + "0x10000000000000000000000000000000FFFF00000000"); + TestBigintAddSubtract("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0x100000000", + "0x1000000000000000000000000000000000000FFFFFFFF"); + TestBigintAddSubtract("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0x10000000000000000000", + "0x10000000000000000000000000FFFFFFFFFFFFFFFFFFF"); + + TestBigintAddSubtract("0xB", "-0x7", "0x4"); + TestBigintAddSubtract("-0xB", "-0x7", "-0x12"); + TestBigintAddSubtract("0xB", "0x7", "0x12"); + TestBigintAddSubtract("-0xB", "0x7", "-0x4"); + TestBigintAddSubtract("-0x7", "0xB", "0x4"); + TestBigintAddSubtract("-0x7", "-0xB", "-0x12"); + TestBigintAddSubtract("0x7", "0xB", "0x12"); + TestBigintAddSubtract("0x7", "-0xB", "-0x4"); +} + + +static void TestBigintShift(const char* a, int amount, const char* result) { + const Bigint& bigint_a = Bigint::Handle(BigintOperations::NewFromCString(a)); + const Bigint& shifted = + Bigint::Handle(BigintOperations::ShiftLeft(bigint_a, amount)); + const char* str_shifted = BigintOperations::ToHexCString(shifted, + &ZoneAllocator); + EXPECT_STREQ(result, str_shifted); + const Bigint& back_shifted = + Bigint::Handle(BigintOperations::ShiftRight(shifted, amount)); + const char* str_back_shifted = BigintOperations::ToHexCString(back_shifted, + &ZoneAllocator); + EXPECT_STREQ(a, str_back_shifted); +} + + +TEST_CASE(BigintLeftShift) { + const char* zero = "0x0"; + const char* one = "0x1"; + const char* minus_one = "-0x1"; + + TestBigintShift(zero, 0, zero); + TestBigintShift(one, 0, one); + TestBigintShift("0x1234", 0, "0x1234"); + TestBigintShift(zero, 100000, zero); + TestBigintShift(one, 1, "0x2"); + TestBigintShift(one, 28, "0x10000000"); + TestBigintShift(one, 32, "0x100000000"); + TestBigintShift(one, 64, "0x10000000000000000"); + TestBigintShift("0x5", 28, "0x50000000"); + TestBigintShift("0x5", 32, "0x500000000"); + TestBigintShift("0x5", 56, "0x500000000000000"); + TestBigintShift("0x5", 64, "0x50000000000000000"); + TestBigintShift("0x5", 128, "0x500000000000000000000000000000000"); + TestBigintShift("0x5", 27, "0x28000000"); + TestBigintShift("0x5", 31, "0x280000000"); + TestBigintShift("0x5", 55, "0x280000000000000"); + TestBigintShift("0x5", 63, "0x28000000000000000"); + TestBigintShift("0x5", 127, "0x280000000000000000000000000000000"); + TestBigintShift("0x8000001", 1, "0x10000002"); + TestBigintShift("0x80000001", 1, "0x100000002"); + TestBigintShift("0x8000000000000001", 1, "0x10000000000000002"); + TestBigintShift("0x8000001", 29, "0x100000020000000"); + TestBigintShift("0x80000001", 33, "0x10000000200000000"); + TestBigintShift("0x8000000000000001", 65, + "0x100000000000000020000000000000000"); + TestBigintShift(minus_one, 0, minus_one); + TestBigintShift("-0x1234", 0, "-0x1234"); + TestBigintShift(minus_one, 1, "-0x2"); + TestBigintShift(minus_one, 28, "-0x10000000"); + TestBigintShift(minus_one, 32, "-0x100000000"); + TestBigintShift(minus_one, 64, "-0x10000000000000000"); + TestBigintShift("-0x5", 28, "-0x50000000"); + TestBigintShift("-0x5", 32, "-0x500000000"); + TestBigintShift("-0x5", 64, "-0x50000000000000000"); + TestBigintShift("-0x5", 27, "-0x28000000"); + TestBigintShift("-0x5", 31, "-0x280000000"); + TestBigintShift("-0x5", 63, "-0x28000000000000000"); + TestBigintShift("-0x8000001", 1, "-0x10000002"); + TestBigintShift("-0x80000001", 1, "-0x100000002"); + TestBigintShift("-0x8000000000000001", 1, "-0x10000000000000002"); + TestBigintShift("-0x8000001", 29, "-0x100000020000000"); + TestBigintShift("-0x80000001", 33, "-0x10000000200000000"); + TestBigintShift("-0x8000000000000001", 65, + "-0x100000000000000020000000000000000"); +} + + +static void TestBigintRightShift( + const char* a, int amount, const char* result) { + const Bigint& bigint_a = Bigint::Handle(BigintOperations::NewFromCString(a)); + const Bigint& shifted = + Bigint::Handle(BigintOperations::ShiftRight(bigint_a, amount)); + const char* str_shifted = BigintOperations::ToHexCString(shifted, + &ZoneAllocator); + if (strcmp(result, str_shifted)) { + WARN2("%s >> %d", a, amount); + } + EXPECT_STREQ(result, str_shifted); +} + + +TEST_CASE(BigintRightShift) { + const char* zero = "0x0"; + const char* one = "0x1"; + const char* minus_one = "-0x1"; + + TestBigintRightShift(one, 1, zero); + TestBigintRightShift(minus_one, 1, minus_one); + TestBigintRightShift("-0x2", 1, minus_one); + TestBigintRightShift("0x12345678", 29, zero); + TestBigintRightShift("-0x12345678", 29, minus_one); + TestBigintRightShift("-0x12345678", 100, minus_one); + TestBigintRightShift("0x5", 1, "0x2"); + TestBigintRightShift("0x5", 2, "0x1"); + TestBigintRightShift("-0x5", 1, "-0x3"); + TestBigintRightShift("-0x5", 2, "-0x2"); + TestBigintRightShift("0x10000001", 28, one); + TestBigintRightShift("0x100000001", 32, one); + TestBigintRightShift("0x10000000000000001", 64, one); + TestBigintRightShift("-0x10000001", 28, "-0x2"); + TestBigintRightShift("-0x100000001", 32, "-0x2"); + TestBigintRightShift("-0x10000000000000001", 64, "-0x2"); + TestBigintRightShift("0x30000000", 29, one); + TestBigintRightShift("0x300000000", 33, one); + TestBigintRightShift("0x30000000000000000", 65, one); + TestBigintRightShift("-0x30000000", 29, "-0x2"); + TestBigintRightShift("-0x300000000", 33, "-0x2"); + TestBigintRightShift("-0x30000000000000000", 65, "-0x2"); +} + + +static void TestBigintBitAnd(const char* a, const char* b, const char* result) { + const Bigint& bigint_a = Bigint::Handle(BigintOperations::NewFromCString(a)); + const Bigint& bigint_b = Bigint::Handle(BigintOperations::NewFromCString(b)); + const Bigint& anded = + Bigint::Handle(BigintOperations::BitAnd(bigint_a, bigint_b)); + const char* str_anded = BigintOperations::ToHexCString(anded, &ZoneAllocator); + EXPECT_STREQ(result, str_anded); + const Bigint& anded2 = + Bigint::Handle(BigintOperations::BitAnd(bigint_b, bigint_a)); + const char* str_anded2 = BigintOperations::ToHexCString(anded2, + &ZoneAllocator); + EXPECT_STREQ(result, str_anded2); + if (BigintOperations::FitsIntoSmi(bigint_a) && !bigint_a.IsNegative()) { + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint_a)); + Smi& smi_anded = Smi::Handle(); + smi_anded ^= BigintOperations::BitAndWithSmi(bigint_b, smi); + const Bigint& anded = + Bigint::Handle(BigintOperations::NewFromSmi(smi_anded)); + const char* str_anded = BigintOperations::ToHexCString(anded, + &ZoneAllocator); + EXPECT_STREQ(result, str_anded); + } + if (BigintOperations::FitsIntoSmi(bigint_b) && !bigint_b.IsNegative()) { + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint_b)); + Smi& smi_anded = Smi::Handle(); + smi_anded ^= BigintOperations::BitAndWithSmi(bigint_a, smi); + const Bigint& anded = + Bigint::Handle(BigintOperations::NewFromSmi(smi_anded)); + const char* str_anded = BigintOperations::ToHexCString(anded, + &ZoneAllocator); + EXPECT_STREQ(result, str_anded); + } +} + + +TEST_CASE(BigintBitAnd) { + const char* zero = "0x0"; + const char* one = "0x1"; + const char* minus_one = "-0x1"; + + TestBigintBitAnd(one, zero, zero); + TestBigintBitAnd(one, one, one); + TestBigintBitAnd(minus_one, zero, zero); + TestBigintBitAnd(minus_one, one, one); + TestBigintBitAnd(minus_one, minus_one, minus_one); + TestBigintBitAnd("0x5", "0x3", one); + TestBigintBitAnd("0x5", minus_one, "0x5"); + TestBigintBitAnd("0x50000000", one, zero); + TestBigintBitAnd("0x50000000", minus_one, "0x50000000"); + TestBigintBitAnd("0x500000000", one, zero); + TestBigintBitAnd("0x500000000", minus_one, "0x500000000"); + TestBigintBitAnd("0x50000000000000000", one, zero); + TestBigintBitAnd("0x50000000000000000", minus_one, "0x50000000000000000"); + TestBigintBitAnd("-0x50000000", "-0x50000000", "-0x50000000"); + TestBigintBitAnd("-0x500000000", "-0x500000000", "-0x500000000"); + TestBigintBitAnd("-0x50000000000000000", + "-0x50000000000000000", + "-0x50000000000000000"); + TestBigintBitAnd("0x1234567890ABCDEF012345678", + "0x876543210FEDCBA0987654321", + "0x224422000A9C9A0002244220"); + TestBigintBitAnd("-0x1234567890ABCDEF012345678", + "-0x876543210FEDCBA0987654321", + "-0x977557799FEFCFEF997755778"); + TestBigintBitAnd("0x1234567890ABCDEF012345678", + "-0x876543210FEDCBA0987654321", + "0x101014589002044F010101458"); + TestBigintBitAnd("0x1234567890ABCDEF012345678FFFFFFFFFFFFFFFFFFFFFFFFF", + "-0x876543210FEDCBA0987654321", + "0x1234567890ABCDEF012345678789ABCDEF012345F6789ABCDF"); + TestBigintBitAnd("0x12345678", "0xFFFFFFF", "0x2345678"); + TestBigintBitAnd("0x123456789", "0xFFFFFFFF", "0x23456789"); + TestBigintBitAnd("-0x10000000", "0xFFFFFFF", "0x0"); + TestBigintBitAnd("-0x100000000", "0xFFFFFFFF", "0x0"); + TestBigintBitAnd("-0x10000001", "0xFFFFFFF", "0xFFFFFFF"); + TestBigintBitAnd("-0x100000001", "0xFFFFFFFF", "0xFFFFFFFF"); + TestBigintBitAnd("-0x10000001", "0x3FFFFFFF", "0x2FFFFFFF"); + TestBigintBitAnd("-0x100000001", "0x3FFFFFFFF", "0x2FFFFFFFF"); + TestBigintBitAnd("-0x10000000000000001", + "0x3FFFFFFFFFFFFFFFF", + "0x2FFFFFFFFFFFFFFFF"); + TestBigintBitAnd("-0x100000000000000", "0xFFFFFFFFFFFFFF", "0x0"); + TestBigintBitAnd("-0x10000000000000000", "0xFFFFFFFFFFFFFFFF", "0x0"); + TestBigintBitAnd("-0x300000000000000", + "0xFFFFFFFFFFFFFFF", + "0xD00000000000000"); + TestBigintBitAnd("-0x30000000000000000", + "0xFFFFFFFFFFFFFFFFF", + "0xD0000000000000000"); + TestBigintBitAnd("-0x10000000", "-0x10000000", "-0x10000000"); + TestBigintBitAnd("-0x100000000", "-0x100000000", "-0x100000000"); + TestBigintBitAnd("-0x100000000000000", + "-0x100000000000000", + "-0x100000000000000"); + TestBigintBitAnd("-0x10000000000000000", + "-0x10000000000000000", + "-0x10000000000000000"); + TestBigintBitAnd("-0x3", "-0x2", "-0x4"); + TestBigintBitAnd("-0x10000000", "-0x10000001", "-0x20000000"); + TestBigintBitAnd("-0x100000000", "-0x100000001", "-0x200000000"); + TestBigintBitAnd("-0x100000000000000", + "-0x100000000000001", + "-0x200000000000000"); + TestBigintBitAnd("-0x10000000000000000", + "-0x10000000000000001", + "-0x20000000000000000"); + TestBigintBitAnd("0x123456789ABCDEF01234567890", + "0x3FFFFFFF", // Max Smi for 32 bits. + "0x34567890"); + TestBigintBitAnd("0x123456789ABCDEF01274567890", + "0x3FFFFFFF", // Max Smi for 32 bits. + "0x34567890"); + TestBigintBitAnd("0x123456789ABCDEF01234567890", + "0x40000000", // Max Smi for 32 bits + 1. + "0x0"); + TestBigintBitAnd("0x123456789ABCDEF01274567890", + "0x40000000", // Max Smi for 32 bits + 1. + "0x40000000"); + TestBigintBitAnd("0x123456789ABCDEF01234567890", + "0x3FFFFFFFFFFFFFFF", // Max Smi for 64 bits. + "0x3CDEF01234567890"); + TestBigintBitAnd("0x123456789ACCDEF01234567890", + "0x4000000000000000", // Max Smi for 64 bits + 1. + "0x4000000000000000"); + TestBigintBitAnd("0x123456789ABCDEF01234567890", + "0x4000000000000000", // Max Smi for 64 bits + 1. + "0x0"); +} + + +static void TestBigintBitOr(const char* a, const char* b, const char* result) { + const Bigint& bigint_a = Bigint::Handle(BigintOperations::NewFromCString(a)); + const Bigint& bigint_b = Bigint::Handle(BigintOperations::NewFromCString(b)); + const Bigint& anded = + Bigint::Handle(BigintOperations::BitOr(bigint_a, bigint_b)); + const char* str_anded = BigintOperations::ToHexCString(anded, &ZoneAllocator); + EXPECT_STREQ(result, str_anded); + const Bigint& anded2 = + Bigint::Handle(BigintOperations::BitOr(bigint_b, bigint_a)); + const char* str_anded2 = BigintOperations::ToHexCString(anded2, + &ZoneAllocator); + EXPECT_STREQ(result, str_anded2); + if (BigintOperations::FitsIntoSmi(bigint_a) && bigint_a.IsNegative()) { + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint_a)); + Smi& smi_ored = Smi::Handle(); + smi_ored ^= BigintOperations::BitOrWithSmi(bigint_b, smi); + const Bigint& ored = + Bigint::Handle(BigintOperations::NewFromSmi(smi_ored)); + const char* str_ored = BigintOperations::ToHexCString(ored, &ZoneAllocator); + EXPECT_STREQ(result, str_ored); + } + if (BigintOperations::FitsIntoSmi(bigint_b) && bigint_b.IsNegative()) { + const Smi& smi = Smi::Handle(BigintOperations::ToSmi(bigint_b)); + Smi& smi_ored = Smi::Handle(); + smi_ored ^= BigintOperations::BitOrWithSmi(bigint_a, smi); + const Bigint& ored = + Bigint::Handle(BigintOperations::NewFromSmi(smi_ored)); + const char* str_ored = BigintOperations::ToHexCString(ored, &ZoneAllocator); + EXPECT_STREQ(result, str_ored); + } +} + + +TEST_CASE(BigintBitOr) { + const char* zero = "0x0"; + const char* one = "0x1"; + const char* minus_one = "-0x1"; + + TestBigintBitOr(one, zero, one); + TestBigintBitOr(one, one, one); + TestBigintBitOr(minus_one, zero, minus_one); + TestBigintBitOr(minus_one, one, minus_one); + TestBigintBitOr(minus_one, minus_one, minus_one); + TestBigintBitOr("-0x3", one, "-0x3"); + TestBigintBitOr("0x5", "0x3", "0x7"); + TestBigintBitOr("0x5", minus_one, minus_one); + TestBigintBitOr("0x5", zero, "0x5"); + TestBigintBitOr("0x50000000", one, "0x50000001"); + TestBigintBitOr("0x50000000", minus_one, minus_one); + TestBigintBitOr("0x500000000", one, "0x500000001"); + TestBigintBitOr("0x500000000", minus_one, minus_one); + TestBigintBitOr("0x50000000000000000", one, "0x50000000000000001"); + TestBigintBitOr("0x50000000000000000", minus_one, minus_one); + TestBigintBitOr("-0x50000000", "-0x50000000", "-0x50000000"); + TestBigintBitOr("-0x500000000", "-0x500000000", "-0x500000000"); + TestBigintBitOr("-0x50000000000000000", + "-0x50000000000000000", + "-0x50000000000000000"); + TestBigintBitOr("0x1234567890ABCDEF012345678", + "0x876543210FEDCBA0987654321", + "0x977557799FEFCFEF997755779"); + TestBigintBitOr("-0x1234567890ABCDEF012345678", + "-0x876543210FEDCBA0987654321", + "-0x224422000A9C9A0002244221"); + TestBigintBitOr("0x1234567890ABCDEF012345678", + "-0x876543210FEDCBA0987654321", + "-0x854101010F440200985410101"); + TestBigintBitOr("0x1234567890ABCDEF012345678FFFFFFFFFFFFFFFFFFFFFFFFF", + "-0x876543210FEDCBA0987654321", + "-0x1"); + TestBigintBitOr("0x12345678", "0xFFFFFFF", "0x1FFFFFFF"); + TestBigintBitOr("0x123456789", "0xFFFFFFFF", "0x1FFFFFFFF"); + TestBigintBitOr("-0x10000000", "0xFFFFFFF", "-0x1"); + TestBigintBitOr("-0x100000000", "0xFFFFFFFF", "-0x1"); + TestBigintBitOr("-0x10000001", "0xFFFFFFF", "-0x10000001"); + TestBigintBitOr("-0x100000001", "0xFFFFFFFF", "-0x100000001"); + TestBigintBitOr("-0x10000001", "0x3FFFFFFF", "-0x1"); + TestBigintBitOr("-0x100000001", "0x3FFFFFFFF", "-0x1"); + TestBigintBitOr("-0x10000000000000001", "0x3FFFFFFFFFFFFFFFF", "-0x1"); + TestBigintBitOr("-0x100000000000000", "0xFFFFFFFFFFFFFF", "-0x1"); + TestBigintBitOr("-0x10000000000000000", "0xFFFFFFFFFFFFFFFF", "-0x1"); + TestBigintBitOr("-0x300000000000000", "0xFFFFFFFFFFFFFFF", "-0x1"); + TestBigintBitOr("-0x30000000000000000", "0xFFFFFFFFFFFFFFFFF", "-0x1"); + TestBigintBitOr("-0x10000000", "-0x10000000", "-0x10000000"); + TestBigintBitOr("-0x100000000", "-0x100000000", "-0x100000000"); + TestBigintBitOr("-0x100000000000000", + "-0x100000000000000", + "-0x100000000000000"); + TestBigintBitOr("-0x10000000000000000", + "-0x10000000000000000", + "-0x10000000000000000"); + TestBigintBitOr("-0x10000000", "-0x10000001", "-0x1"); + TestBigintBitOr("-0x100000000", "-0x100000001", "-0x1"); + TestBigintBitOr("-0x100000000000000", "-0x100000000000001", "-0x1"); + TestBigintBitOr("-0x10000000000000000", "-0x10000000000000001", "-0x1"); +} + + +static void TestBigintBitXor(const char* a, const char* b, const char* result) { + const Bigint& bigint_a = Bigint::Handle(BigintOperations::NewFromCString(a)); + const Bigint& bigint_b = Bigint::Handle(BigintOperations::NewFromCString(b)); + const Bigint& xored = + Bigint::Handle(BigintOperations::BitXor(bigint_a, bigint_b)); + const char* str_xored = BigintOperations::ToHexCString(xored, &ZoneAllocator); + EXPECT_STREQ(result, str_xored); + const Bigint& xored2 = + Bigint::Handle(BigintOperations::BitXor(bigint_b, bigint_a)); + const char* str_xored2 = BigintOperations::ToHexCString(xored2, + &ZoneAllocator); + EXPECT_STREQ(result, str_xored2); +} + + +TEST_CASE(BigintBitXor) { + const char* zero = "0x0"; + const char* one = "0x1"; + const char* minus_one = "-0x1"; + + TestBigintBitXor(one, zero, one); + TestBigintBitXor(one, one, zero); + TestBigintBitXor(minus_one, zero, minus_one); + TestBigintBitXor(minus_one, one, "-0x2"); + TestBigintBitXor(minus_one, minus_one, zero); + TestBigintBitXor("0x5", "0x3", "0x6"); + TestBigintBitXor("0x5", minus_one, "-0x6"); + TestBigintBitXor("0x5", zero, "0x5"); + TestBigintBitXor(minus_one, "-0x8", "0x7"); + TestBigintBitXor("0x50000000", one, "0x50000001"); + TestBigintBitXor("0x50000000", minus_one, "-0x50000001"); + TestBigintBitXor("0x500000000", one, "0x500000001"); + TestBigintBitXor("0x500000000", minus_one, "-0x500000001"); + TestBigintBitXor("0x50000000000000000", one, "0x50000000000000001"); + TestBigintBitXor("0x50000000000000000", minus_one, "-0x50000000000000001"); + TestBigintBitXor("-0x50000000", "-0x50000000", zero); + TestBigintBitXor("-0x500000000", "-0x500000000", zero); + TestBigintBitXor("-0x50000000000000000", "-0x50000000000000000", zero); + TestBigintBitXor("0x1234567890ABCDEF012345678", + "0x876543210FEDCBA0987654321", + "0x955115599F46064F995511559"); + TestBigintBitXor("-0x1234567890ABCDEF012345678", + "-0x876543210FEDCBA0987654321", + "0x955115599F46064F995511557"); + TestBigintBitXor("0x1234567890ABCDEF012345678", + "-0x876543210FEDCBA0987654321", + "-0x955115599F46064F995511559"); + TestBigintBitXor("0x1234567890ABCDEF012345678FFFFFFFFFFFFFFFFFFFFFFFFF", + "-0x876543210FEDCBA0987654321", + "-0x1234567890ABCDEF012345678789ABCDEF012345F6789ABCE0"); + TestBigintBitXor("0x12345678", "0xFFFFFFF", "0x1DCBA987"); + TestBigintBitXor("0x123456789", "0xFFFFFFFF", "0x1DCBA9876"); + TestBigintBitXor("-0x10000000", "0xFFFFFFF", "-0x1"); + TestBigintBitXor("-0x100000000", "0xFFFFFFFF", "-0x1"); + TestBigintBitXor("-0x10000001", "0xFFFFFFF", "-0x20000000"); + TestBigintBitXor("-0x100000001", "0xFFFFFFFF", "-0x200000000"); + TestBigintBitXor("-0x10000001", "0x3FFFFFFF", "-0x30000000"); + TestBigintBitXor("-0x100000001", "0x3FFFFFFFF", "-0x300000000"); + TestBigintBitXor("-0x10000000000000001", + "0x3FFFFFFFFFFFFFFFF", + "-0x30000000000000000"); + TestBigintBitXor("-0x100000000000000", "0xFFFFFFFFFFFFFF", "-0x1"); + TestBigintBitXor("-0x10000000000000000", "0xFFFFFFFFFFFFFFFF", "-0x1"); + TestBigintBitXor("-0x300000000000000", + "0xFFFFFFFFFFFFFFF", + "-0xD00000000000001"); + TestBigintBitXor("-0x30000000000000000", + "0xFFFFFFFFFFFFFFFFF", + "-0xD0000000000000001"); + TestBigintBitXor("-0x10000000", "-0x10000000", zero); + TestBigintBitXor("-0x100000000", "-0x100000000", zero); + TestBigintBitXor("-0x100000000000000", "-0x100000000000000", zero); + TestBigintBitXor("-0x10000000000000000", "-0x10000000000000000", zero); + TestBigintBitXor("-0x10000000", "-0x10000001", "0x1FFFFFFF"); + TestBigintBitXor("-0x100000000", "-0x100000001", "0x1FFFFFFFF"); + TestBigintBitXor("-0x100000000000000", + "-0x100000000000001", + "0x1FFFFFFFFFFFFFF"); + TestBigintBitXor("-0x10000000000000000", + "-0x10000000000000001", + "0x1FFFFFFFFFFFFFFFF"); +} + + +static void TestBigintBitNot(const char* a, const char* result) { + const Bigint& bigint_a = Bigint::Handle(BigintOperations::NewFromCString(a)); + const Bigint& inverted = + Bigint::Handle(BigintOperations::BitNot(bigint_a)); + const char* str_inverted = BigintOperations::ToHexCString(inverted, + &ZoneAllocator); + EXPECT_STREQ(result, str_inverted); + const Bigint& back = + Bigint::Handle(BigintOperations::BitNot(inverted)); + const char* str_back = BigintOperations::ToHexCString(back, &ZoneAllocator); + EXPECT_STREQ(a, str_back); +} + + +TEST_CASE(BigintBitNot) { + const char* zero = "0x0"; + const char* one = "0x1"; + const char* minus_one = "-0x1"; + + TestBigintBitNot(zero, minus_one); + TestBigintBitNot(one, "-0x2"); + TestBigintBitNot("0x5", "-0x6"); + TestBigintBitNot("0x50000000", "-0x50000001"); + TestBigintBitNot("0xFFFFFFF", "-0x10000000"); + TestBigintBitNot("0xFFFFFFFF", "-0x100000000"); + TestBigintBitNot("0xFFFFFFFFFFFFFF", "-0x100000000000000"); + TestBigintBitNot("0xFFFFFFFFFFFFFFFF", "-0x10000000000000000"); + TestBigintBitNot("0x1234567890ABCDEF012345678", + "-0x1234567890ABCDEF012345679"); +} + + +static void TestBigintMultiplyDivide(const char* a, + const char* b, + const char* product) { + const Bigint& bigint_a = Bigint::Handle(BigintOperations::NewFromCString(a)); + const Bigint& bigint_b = Bigint::Handle(BigintOperations::NewFromCString(b)); + const Bigint& computed_product = + Bigint::Handle(BigintOperations::Multiply(bigint_a, bigint_b)); + const char* str_product = BigintOperations::ToHexCString(computed_product, + &ZoneAllocator); + EXPECT_STREQ(product, str_product); + const Bigint& computed_product2 = + Bigint::Handle(BigintOperations::Multiply(bigint_b, bigint_a)); + const char* str_product2 = BigintOperations::ToHexCString(computed_product2, + &ZoneAllocator); + EXPECT_STREQ(product, str_product2); + + const Bigint& bigint_product = + Bigint::Handle(BigintOperations::NewFromCString(product)); + if (!bigint_a.IsZero()) { + const Bigint& computed_quotient1 = + Bigint::Handle(BigintOperations::Divide(bigint_product, bigint_a)); + const char* str_quotient1 = + BigintOperations::ToHexCString(computed_quotient1, &ZoneAllocator); + EXPECT_STREQ(b, str_quotient1); + } + + if (!bigint_b.IsZero()) { + const Bigint& computed_quotient2 = + Bigint::Handle(BigintOperations::Divide(bigint_product, bigint_b)); + const char* str_quotient2 = + BigintOperations::ToHexCString(computed_quotient2, &ZoneAllocator); + EXPECT_STREQ(a, str_quotient2); + } +} + + +TEST_CASE(BigintMultiplyDivide) { + const char* zero = "0x0"; + const char* one = "0x1"; + const char* minus_one = "-0x1"; + + TestBigintMultiplyDivide(zero, zero, zero); + TestBigintMultiplyDivide(one, one, one); + TestBigintMultiplyDivide(one, zero, zero); + TestBigintMultiplyDivide(zero, one, zero); + TestBigintMultiplyDivide(one, minus_one, minus_one); + TestBigintMultiplyDivide(minus_one, minus_one, one); + TestBigintMultiplyDivide("0x42", one, "0x42"); + TestBigintMultiplyDivide("0x42", "0x2", "0x84"); + TestBigintMultiplyDivide("0xFFFF", "0x2", "0x1FFFE"); + TestBigintMultiplyDivide("0x3", "0x5", "0xF"); + TestBigintMultiplyDivide("0xFFFFF", "0x5", "0x4FFFFB"); + TestBigintMultiplyDivide("0xFFFFFFF", "0x5", "0x4FFFFFFB"); + TestBigintMultiplyDivide("0xFFFFFFFF", "0x5", "0x4FFFFFFFB"); + TestBigintMultiplyDivide("0xFFFFFFFFFFFFFFFF", "0x5", "0x4FFFFFFFFFFFFFFFB"); + TestBigintMultiplyDivide("0xFFFFFFFFFFFFFFFF", "0x3039", + "0x3038FFFFFFFFFFFFCFC7"); + TestBigintMultiplyDivide("0xFFFFFFFFFFFFFFFF", + "0xFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0xFFFFFFFFFFFFFFFEFFFFFFFFFF0000000000000001"); + TestBigintMultiplyDivide( + "0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000", + "0xFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000", + "0xFFFFFFFFFFFFFFFEFFFFFFFFFF000000000000000100000000000000" + "000000000000000000000000000000000000000000000000000000000000"); + TestBigintMultiplyDivide("0x10000001", "0x5", "0x50000005"); + TestBigintMultiplyDivide( + "0x1234567890ABCDEF01234567890ABCDEF01234567890ABCDEF" + "01234567890ABCDEF01234567890ABCDEF", + "0x1234567890ABCDEF01234567890ABCDEF01234567890ABCDEF" + "01234567890ABCDEF01234567890ABCDEF", + "0x14B66DC328828BCA670CBE52943AA3894CCCE15C8F5ED1E55F" + "328F6D3F579F992299850C4B5B95213EF3FB7B4E73B5F43D4299" + "5B9F6FD5441C275F2FF89F86F28F47A94CA37481090DCCCDCA6475F09A2F2A521"); + TestBigintMultiplyDivide( + "0x1234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF" + "01234567890ABCDEF", + "0x1234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "90123456789012345678901234567890", + "0x14B66DC327D3C88D7EAA988BBFFA9BBA877826E7EDAF373907A931FBFC3A25231DF7F2" + "516F511FB1638F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A" + "8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F" + "0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B" + "570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B57" + "0F4A8F0B570F4A8F0B570F4A8F0B570F35D89D93E776C67DD864B2034B5C739007933027" + "5CDFD41E07A15D0F5AD5256BED5F1CF91FBA375DE70"); + TestBigintMultiplyDivide( + "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFF", + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000001"); + TestBigintMultiplyDivide( + "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFF", + "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFF", + "0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFC0000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000001"); + + // A 256 28-bit digits number squared. + TestBigintMultiplyDivide( + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000001"); + + + TestBigintMultiplyDivide( + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000001"); +} + + +static void TestBigintDivideRemainder(const char* a, + const char* b, + const char* quotient, + const char* remainder) { + const Bigint& bigint_a = Bigint::Handle(BigintOperations::NewFromCString(a)); + const Bigint& bigint_b = Bigint::Handle(BigintOperations::NewFromCString(b)); + const Bigint& computed_quotient = + Bigint::Handle(BigintOperations::Divide(bigint_a, bigint_b)); + const Bigint& computed_remainder = + Bigint::Handle(BigintOperations::Remainder(bigint_a, bigint_b)); + const char* str_quotient = BigintOperations::ToHexCString(computed_quotient, + &ZoneAllocator); + const char* str_remainder = + BigintOperations::ToHexCString(computed_remainder, &ZoneAllocator); + EXPECT_STREQ(quotient, str_quotient); + EXPECT_STREQ(remainder, str_remainder); +} + + +TEST_CASE(BigintDivideRemainder) { + const char* zero = "0x0"; + const char* one = "0x1"; + const char* minus_one = "-0x1"; + + TestBigintDivideRemainder(one, one, one, zero); + TestBigintDivideRemainder(zero, one, zero, zero); + TestBigintDivideRemainder(minus_one, one, minus_one, zero); + TestBigintDivideRemainder(one, "0x2", zero, one); + TestBigintDivideRemainder(minus_one, "0x7", zero, minus_one); + TestBigintDivideRemainder("0xB", "0x7", one, "0x4"); + TestBigintDivideRemainder("0x12345678", "0x7", "0x299C335", "0x5"); + TestBigintDivideRemainder("-0x12345678", "0x7", "-0x299C335", "-0x5"); + TestBigintDivideRemainder("0x12345678", "-0x7", "-0x299C335", "0x5"); + TestBigintDivideRemainder("-0x12345678", "-0x7", "0x299C335", "-0x5"); + TestBigintDivideRemainder("0x7", "0x12345678", zero, "0x7"); + TestBigintDivideRemainder("-0x7", "0x12345678", zero, "-0x7"); + TestBigintDivideRemainder("-0x7", "-0x12345678", zero, "-0x7"); + TestBigintDivideRemainder("0x7", "-0x12345678", zero, "0x7"); + TestBigintDivideRemainder("0x12345678", "0x7", "0x299C335", "0x5"); + TestBigintDivideRemainder("-0x12345678", "0x7", "-0x299C335", "-0x5"); + TestBigintDivideRemainder("0x12345678", "-0x7", "-0x299C335", "0x5"); + TestBigintDivideRemainder("-0x12345678", "-0x7", "0x299C335", "-0x5"); + TestBigintDivideRemainder( + "0x14B66DC327D3C88D7EAA988BBFFA9BBA877826E7EDAF373907A931FBFC3A25231DF7F2" + "516F511FB1638F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A" + "8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F" + "0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B" + "570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B57" + "0F4A8F0B570F4A8F0B570F4A8F0B570F35D89D93E776C67DD864B2034B5C739007933027" + "5CDFD41E07A15D0F5AD5256BED5F1CF91FBA375DE70", + "0x1234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF" + "01234567890ABCDEF", + "0x1234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "90123456789012345678901234567890", + zero); + TestBigintDivideRemainder( + "0x14B66DC327D3C88D7EAA988BBFFA9BBA877826E7EDAF373907A931FBFC3A25231DF7F2" + "516F511FB1638F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A" + "8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F" + "0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B" + "570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B57" + "0F4A8F0B570F4A8F0B570F4A8F0B570F35D89D93E776C67DD864B2034B5C739007933027" + "5CDFD41E07A15D0F5AD5256BED5F1CF91FBA375DE71", + "0x1234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF" + "01234567890ABCDEF", + "0x1234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "90123456789012345678901234567890", + one); + TestBigintDivideRemainder( + "0x14B66DC327D3C88D7EAA988BBFFA9BBA877826E7EDAF373907A931FBFC3A25231DF7F2" + "516F511FB1638F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A" + "8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F" + "0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B" + "570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B570F4A8F0B57" + "0F4A8F0B570F4A8F0B570F4A8F0B5710591E051CF233A56DEA99087BDC08417F08B6758E" + "E5EA90FCF7B39165D365D139DC60403E8743421AC5E", + "0x1234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF" + "01234567890ABCDEF", + "0x1234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "90123456789012345678901234567890", + "0x1234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF" + "01234567890ABCDEE"); +} + +} // namespace dart diff --git a/runtime/vm/bigint_store.h b/runtime/vm/bigint_store.h new file mode 100644 index 00000000000..4c8ebc940fc --- /dev/null +++ b/runtime/vm/bigint_store.h @@ -0,0 +1,36 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_BIGINT_STORE_H_ +#define VM_BIGINT_STORE_H_ + +#include "vm/globals.h" + +namespace dart { + +// Use to store teporary BIGNUMs. +class BigintStore { + public: + BigintStore() : bn_(NULL), bn_ctx_(NULL) {} + ~BigintStore() { + BN_free(bn_); + BN_CTX_free(bn_ctx_); + } + + static BigintStore* Get() { + BigintStore* store = Isolate::Current()->bigint_store(); + if (store == NULL) { + store = new BigintStore(); + Isolate::Current()->set_bigint_store(store); + } + return store; + } + + BIGNUM* bn_; + BN_CTX* bn_ctx_; +}; + +} // namespace dart + +#endif // VM_BIGINT_STORE_H_ diff --git a/runtime/vm/bitfield.h b/runtime/vm/bitfield.h new file mode 100644 index 00000000000..7fa35c2ac81 --- /dev/null +++ b/runtime/vm/bitfield.h @@ -0,0 +1,65 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_BITFIELD_H_ +#define VM_BITFIELD_H_ + +namespace dart { + +// BitField is a template for encoding and decoding a bit field inside +// an unsigned machine word. +template +class BitField { + public: + // Tells whether the provided value fits into the bit field. + static bool is_valid(T value) { + return (static_cast(value) & ~((1U << size) - 1)) == 0; + } + + // Returns a uword mask of the bit field. + static uword mask() { + return (1U << size) - 1; + } + + // Returns a uword mask of the bit field which can be applied directly to + // to the raw unshifted bits. + static uword mask_in_place() { + return ((1U << size) - 1) << position; + } + + // Returns the shift count needed to right-shift the bit field to + // the least-significant bits. + static int shift() { + return position; + } + + // Returns the size of the bit field. + static int bitsize() { + return size; + } + + // Returns a uword with the bit field value encoded. + static uword encode(T value) { + ASSERT(is_valid(value)); + return static_cast(value) << position; + } + + // Extracts the bit field from the value. + static T decode(uword value) { + return static_cast((value >> position) & ((1U << size) - 1)); + } + + // Returns a uword with the bit field value encoded based on the + // original value. Only the bits corresponding to this bit field + // will be changed. + static uword update(T value, uword original) { + ASSERT(is_valid(value)); + return (static_cast(value) << position) | + (~mask_in_place() & original); + } +}; + +} // namespace dart + +#endif // VM_BITFIELD_H_ diff --git a/runtime/vm/bitfield_test.cc b/runtime/vm/bitfield_test.cc new file mode 100644 index 00000000000..49a6ae22412 --- /dev/null +++ b/runtime/vm/bitfield_test.cc @@ -0,0 +1,24 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/bitfield.h" +#include "vm/unit_test.h" + +namespace dart { + +UNIT_TEST_CASE(BitFields) { + class TestBitFields : public BitField {}; + EXPECT(TestBitFields::is_valid(16)); + EXPECT(!TestBitFields::is_valid(256)); + EXPECT_EQ(0x00ffU, TestBitFields::mask()); + EXPECT_EQ(0x001feU, TestBitFields::mask_in_place()); + EXPECT_EQ(1, TestBitFields::shift()); + EXPECT_EQ(8, TestBitFields::bitsize()); + EXPECT_EQ(32U, TestBitFields::encode(16)); + EXPECT_EQ(16, TestBitFields::decode(32)); + EXPECT_EQ(2U, TestBitFields::update(1, 16)); +} + +} // namespace dart diff --git a/runtime/vm/boolfield.h b/runtime/vm/boolfield.h new file mode 100644 index 00000000000..23761d9e509 --- /dev/null +++ b/runtime/vm/boolfield.h @@ -0,0 +1,39 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_BOOLFIELD_H_ +#define VM_BOOLFIELD_H_ + +namespace dart { + +// BoolField is a template for encoding and decoding a bit inside an +// unsigned machine word. +template +class BoolField { + public: + // Returns a uword with the bool value encoded. + static uword encode(bool value) { + ASSERT(position < sizeof(uword)); + return static_cast((value ? 1U : 0) << position); + } + + // Extracts the bool from the value. + static bool decode(uword value) { + ASSERT(position < sizeof(uword)); + return (value & (1U << position)) != 0; + } + + // Returns a uword with the bool field value encoded based on the + // original value. Only the single bit corresponding to this bool + // field will be changed. + static uword update(bool value, uword original) { + ASSERT(position < sizeof(uword)); + const uword mask = 1U << position; + return value ? original | mask : original & ~mask; + } +}; + +} // namespace dart + +#endif // VM_BOOLFIELD_H_ diff --git a/runtime/vm/boolfield_test.cc b/runtime/vm/boolfield_test.cc new file mode 100644 index 00000000000..a1949420baf --- /dev/null +++ b/runtime/vm/boolfield_test.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/boolfield.h" +#include "vm/unit_test.h" + +namespace dart { + +UNIT_TEST_CASE(BoolField) { + class TestBoolField : public BoolField<1> {}; + EXPECT(TestBoolField::decode(2)); + EXPECT(!TestBoolField::decode(1)); + EXPECT_EQ(2U, TestBoolField::encode(true)); + EXPECT_EQ(0U, TestBoolField::encode(false)); + EXPECT_EQ(3U, TestBoolField::update(true, 1)); + EXPECT_EQ(1U, TestBoolField::update(false, 1)); +} + +} // namespace dart diff --git a/runtime/vm/bootstrap.cc b/runtime/vm/bootstrap.cc new file mode 100644 index 00000000000..be974da4c23 --- /dev/null +++ b/runtime/vm/bootstrap.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bootstrap.h" + +#include "include/dart_api.h" + +#include "vm/bootstrap_natives.h" +#include "vm/compiler.h" +#include "vm/dart_api_impl.h" +#include "vm/object.h" +#include "vm/object_store.h" + +namespace dart { + +DEFINE_FLAG(bool, print_bootstrap, false, "Print the bootstrap source."); + +// List all native functions implemented in the vm or core bootstrap dart +// libraries so that we can resolve the native function to it's entry +// point. +static struct NativeEntries { + const char* name_; + Dart_NativeFunction function_; + int argument_count_; +} BootStrapEntries[] = { + BOOTSTRAP_NATIVE_LIST(REGISTER_NATIVE_ENTRY) +}; + + +static Dart_NativeFunction native_lookup(Dart_Handle name, + int argument_count) { + const Object& obj = Object::Handle(Api::UnwrapHandle(name)); + if (!obj.IsString()) { + return NULL; + } + const char* function_name = obj.ToCString(); + ASSERT(function_name != NULL); + int num_entries = sizeof(BootStrapEntries) / sizeof(struct NativeEntries); + for (int i = 0; i < num_entries; i++) { + struct NativeEntries* entry = &(BootStrapEntries[i]); + if (!strncmp(function_name, entry->name_, strlen(entry->name_))) { + if (entry->argument_count_ == argument_count) { + return reinterpret_cast(entry->function_); + } else { + // Wrong number of arguments. + // TODO(regis): Should we pass a buffer for error reporting? + return NULL; + } + } + } + return NULL; +} + + +RawScript* Bootstrap::LoadScript() { + const String& url = String::Handle(String::New("bootstrap", Heap::kOld)); + const String& src = String::Handle(String::New(corelib_source_, Heap::kOld)); + + const Script& result = + Script::Handle(Script::New(url, src, RawScript::kSource)); + return result.raw(); +} + + +RawScript* Bootstrap::LoadImplScript() { + const String& url = String::Handle(String::New("bootstrap_impl", + Heap::kOld)); + const String& src = String::Handle(String::New(corelib_impl_source_, + Heap::kOld)); + + const Script& result = + Script::Handle(Script::New(url, src, RawScript::kSource)); + return result.raw(); +} + + +void Bootstrap::Compile(const Library& library, const Script& script) { + if (FLAG_print_bootstrap) { + OS::Print("Bootstrap source '%s':\n%s\n", + String::Handle(script.url()).ToCString(), + String::Handle(script.source()).ToCString()); + } + Compiler::Compile(library, script); +} + + +void Bootstrap::SetupNativeResolver() { + Library& library = Library::Handle(Library::CoreLibrary()); + ASSERT(!library.IsNull()); + library.set_native_entry_resolver( + reinterpret_cast(native_lookup)); + library = Library::CoreImplLibrary(); + ASSERT(!library.IsNull()); + library.set_native_entry_resolver( + reinterpret_cast(native_lookup)); +} + +} // namespace dart diff --git a/runtime/vm/bootstrap.h b/runtime/vm/bootstrap.h new file mode 100644 index 00000000000..96cfbc75b44 --- /dev/null +++ b/runtime/vm/bootstrap.h @@ -0,0 +1,31 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_BOOTSTRAP_H_ +#define VM_BOOTSTRAP_H_ + +#include "vm/allocation.h" + +namespace dart { + +// Forward declarations. +class Library; +class RawScript; +class Script; + +class Bootstrap : public AllStatic { + public: + static RawScript* LoadScript(); + static RawScript* LoadImplScript(); + static void Compile(const Library& library, const Script& script); + static void SetupNativeResolver(); + + private: + static const char corelib_source_[]; + static const char corelib_impl_source_[]; +}; + +} // namespace dart + +#endif // VM_BOOTSTRAP_H_ diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h new file mode 100644 index 00000000000..f17a22be6da --- /dev/null +++ b/runtime/vm/bootstrap_natives.h @@ -0,0 +1,106 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_BOOTSTRAP_NATIVES_H_ +#define VM_BOOTSTRAP_NATIVES_H_ + +#include "vm/native_entry.h" + +// bootstrap dart natives used in the core dart library. + +namespace dart { + +// List of bootstrap native entry points used in the core dart library. +#define BOOTSTRAP_NATIVE_LIST(V) \ + V(Object_toString, 1) \ + V(Object_noSuchMethod, 3) \ + V(Integer_bitAndFromInteger, 2) \ + V(Integer_bitOrFromInteger, 2) \ + V(Integer_bitXorFromInteger, 2) \ + V(Integer_addFromInteger, 2) \ + V(Integer_subFromInteger, 2) \ + V(Integer_mulFromInteger, 2) \ + V(Integer_truncDivFromInteger, 2) \ + V(Integer_moduloFromInteger, 2) \ + V(Integer_greaterThanFromInteger, 2) \ + V(Integer_equalToInteger, 2) \ + V(IsolateNatives_start, 2) \ + V(ReceivePortImpl_factory, 1) \ + V(ReceivePortImpl_closeInternal, 1) \ + V(SendPortImpl_sendInternal_, 3) \ + V(Smi_shlFromInt, 2) \ + V(Smi_sarFromInt, 2) \ + V(Smi_bitNegate, 1) \ + V(Mint_bitNegate, 1) \ + V(Bigint_bitNegate, 1) \ + V(Double_add, 2) \ + V(Double_sub, 2) \ + V(Double_mul, 2) \ + V(Double_div, 2) \ + V(Double_trunc_div, 2) \ + V(Double_remainder, 2) \ + V(Double_modulo, 2) \ + V(Double_greaterThanFromInteger, 2) \ + V(Double_equalToInteger, 2) \ + V(Double_greaterThan, 2) \ + V(Double_equal, 2) \ + V(Double_isNegative, 1) \ + V(Double_isInfinite, 1) \ + V(Double_isNaN, 1) \ + V(Double_doubleFromInteger, 2) \ + V(Double_round, 1) \ + V(Double_floor, 1) \ + V(Double_ceil, 1) \ + V(Double_truncate, 1) \ + V(Double_toInt, 1) \ + V(Double_pow, 2) \ + V(JSSyntaxRegExp_factory, 3) \ + V(JSSyntaxRegExp_getPattern, 1) \ + V(JSSyntaxRegExp_getFlags, 1) \ + V(JSSyntaxRegExp_getGroupCount, 1) \ + V(JSSyntaxRegExp_ExecuteMatch, 3) \ + V(ObjectArray_allocate, 2) \ + V(ObjectArray_getIndexed, 2) \ + V(ObjectArray_setIndexed, 3) \ + V(ObjectArray_getLength, 1) \ + V(ObjectArray_copyFromObjectArray, 5) \ + V(StringBase_createFromCodePoints, 1) \ + V(String_hashCode, 1) \ + V(String_getLength, 1) \ + V(String_charAt, 2) \ + V(String_charCodeAt, 2) \ + V(String_concat, 2) \ + V(Strings_concatAll, 1) \ + V(MathNatives_sqrt, 1) \ + V(MathNatives_sin, 1) \ + V(MathNatives_cos, 1) \ + V(MathNatives_tan, 1) \ + V(MathNatives_asin, 1) \ + V(MathNatives_acos, 1) \ + V(MathNatives_atan, 1) \ + V(MathNatives_2atan, 2) \ + V(MathNatives_exp, 1) \ + V(MathNatives_log, 1) \ + V(MathNatives_random, 0) \ + V(MathNatives_parseInt, 1) \ + V(MathNatives_parseDouble, 1) \ + V(DateTimeNatives_brokenDownToSecondsSinceEpoch, 7) \ + V(DateTimeNatives_currentTimeMillis, 0) \ + V(DateTimeNatives_getYear, 2) \ + V(DateTimeNatives_getMonth, 2) \ + V(DateTimeNatives_getDay, 2) \ + V(DateTimeNatives_getHours, 2) \ + V(DateTimeNatives_getMinutes, 2) \ + V(DateTimeNatives_getSeconds, 2) \ + V(AssertError_throwNew, 2) \ + V(FallThroughError_throwNew, 1) \ + V(Clock_now, 0) \ + V(Clock_frequency, 0) \ + + +BOOTSTRAP_NATIVE_LIST(DECLARE_NATIVE_ENTRY) + +} // namespace dart + +#endif // VM_BOOTSTRAP_NATIVES_H_ diff --git a/runtime/vm/c99_support_win.h b/runtime/vm/c99_support_win.h new file mode 100644 index 00000000000..96d4a3a773d --- /dev/null +++ b/runtime/vm/c99_support_win.h @@ -0,0 +1,52 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_C99_SUPPORT_WIN_H_ +#define VM_C99_SUPPORT_WIN_H_ + +// Visual C++ is missing a bunch of C99 math macros and +// functions. Define them here. + +#include + +static const unsigned __int64 kQuietNaNMask = + static_cast(0xfff) << 51; + +#define INFINITY HUGE_VAL +#define NAN \ + *reinterpret_cast(&kQuietNaNMask) + +static inline int isinf(double x) { + return (_fpclass(x) & (_FPCLASS_PINF | _FPCLASS_NINF)) != 0; +} + +static inline int isnan(double x) { + return _isnan(x); +} + +static inline int signbit(double x) { + if (x == 0) { + return _fpclass(x) & _FPCLASS_NZ; + } else { + return x < 0; + } +} + +static inline double trunc(double x) { + if (x < 0) { + return ceil(x); + } else { + return floor(x); + } +} + +static inline double round(double x) { + if (x < 0) { + return ceil(x - 0.5); + } else { + return floor(x + 0.5); + } +} + +#endif // VM_C99_SUPPORT_WIN_H_ diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc new file mode 100644 index 00000000000..009f7ca5117 --- /dev/null +++ b/runtime/vm/class_finalizer.cc @@ -0,0 +1,933 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/class_finalizer.h" + +#include "vm/flags.h" +#include "vm/heap.h" +#include "vm/isolate.h" +#include "vm/longjump.h" +#include "vm/object_store.h" + +namespace dart { + +DEFINE_FLAG(bool, print_classes, false, "Prints details about loaded classes."); +DEFINE_FLAG(bool, trace_class_finalization, false, "Trace class finalization."); +DEFINE_FLAG(bool, verify_implements, false, + "Verify that all classes implement their interface."); +DECLARE_FLAG(bool, enable_type_checks); + +void ClassFinalizer::AddPendingClasses( + const GrowableArray& classes) { + if (!classes.is_empty()) { + ObjectStore* object_store = Isolate::Current()->object_store(); + const Array& old_array = Array::Handle(object_store->pending_classes()); + const intptr_t old_length = old_array.Length(); + const int new_length = old_length + classes.length(); + const Array& new_array = Array::Handle(Array::Grow(old_array, new_length)); + // Add new classes. + for (int i = 0; i < classes.length(); i++) { + new_array.SetAt(i + old_length, *classes[i]); + } + object_store->set_pending_classes(new_array); + } +} + + +bool ClassFinalizer::AllClassesFinalized() { + ObjectStore* object_store = Isolate::Current()->object_store(); + const Array& classes = Array::Handle(object_store->pending_classes()); + return classes.Length() == 0; +} + + +// Class finalization occurs: +// a) when bootstrap process completes (VerifyBootstrapClasses). +// b) after the user classes are loaded (dart_api). +bool ClassFinalizer::FinalizePendingClasses() { + bool retval = true; + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ObjectStore* object_store = isolate->object_store(); + const String& error = String::Handle(object_store->sticky_error()); + if (!error.IsNull()) { + return false; + } + LongJump* base = isolate->long_jump_base(); + LongJump jump; + isolate->set_long_jump_base(&jump); + if (setjmp(*jump.Set()) == 0) { + const Array& class_array = Array::Handle(object_store->pending_classes()); + ASSERT(!class_array.IsNull()); + Class& cls = Class::Handle(); + // First resolve all superclasses. + for (intptr_t i = 0; i < class_array.Length(); i++) { + cls ^= class_array.At(i); + if (FLAG_trace_class_finalization) { + OS::Print("Resolving super and default: %s\n", cls.ToCString()); + } + ResolveSuperClass(cls); + if (cls.is_interface()) { + ResolveDefaultClass(cls); + } + } + // Finalize all classes. + for (intptr_t i = 0; i < class_array.Length(); i++) { + cls ^= class_array.At(i); + FinalizeClass(cls); + } + if (FLAG_print_classes) { + for (intptr_t i = 0; i < class_array.Length(); i++) { + cls ^= class_array.At(i); + PrintClassInformation(cls); + } + } + if (FLAG_verify_implements) { + for (intptr_t i = 0; i < class_array.Length(); i++) { + cls ^= class_array.At(i); + if (!cls.is_interface()) { + VerifyClassImplements(cls); + } + } + } + // Clear pending classes array. + object_store->set_pending_classes(Array::Handle(Array::Empty())); + } else { + retval = false; + } + isolate->set_long_jump_base(base); + return retval; +} + + +#if defined (DEBUG) +// Adds all interfaces of cls into 'collected'. Duplicate entries may occur. +// No cycles are allowed. +void ClassFinalizer::CollectInterfaces(const Class& cls, + GrowableArray* collected) { + const Array& interface_array = Array::ZoneHandle(cls.interfaces()); + for (intptr_t i = 0; i < interface_array.Length(); i++) { + Type& interface = Type::Handle(); + interface ^= interface_array.At(i); + const Class& interface_class = Class::ZoneHandle(interface.type_class()); + collected->Add(&interface_class); + CollectInterfaces(interface_class, collected); + } +} + + +// Collect all interfaces of the class 'cls' and check that every function +// defined in each interface can be found in the class. +// No need to check instance fields since they have been turned into +// getters/setters. +void ClassFinalizer::VerifyClassImplements(const Class& cls) { + ASSERT(!cls.is_interface()); + GrowableArray interfaces; + CollectInterfaces(cls, &interfaces); + for (int i = 0; i < interfaces.length(); i++) { + const String& interface_name = String::Handle(interfaces[i]->Name()); + const Array& interface_functions = + Array::Handle(interfaces[i]->functions()); + for (intptr_t f = 0; f < interface_functions.Length(); f++) { + Function& interface_function = Function::Handle(); + interface_function ^= interface_functions.At(f); + const String& function_name = String::Handle(interface_function.name()); + // Check for constructor/factory. + if (function_name.StartsWith(interface_name)) { + // TODO(srdjan): convert 'InterfaceName.' to 'ClassName.' and check. + continue; + } + if (interface_function.kind() == RawFunction::kConstImplicitGetter) { + // This interface constants are not overridable. + continue; + } + // Lookup function in 'cls' and all its super classes. + Class& test_class = Class::Handle(cls.raw()); + Function& class_function = + Function::Handle(test_class.LookupDynamicFunction(function_name)); + while (class_function.IsNull()) { + test_class = test_class.SuperClass(); + if (test_class.IsNull()) break; + class_function = test_class.LookupDynamicFunction(function_name); + } + if (class_function.IsNull()) { + OS::Print("%s implements '%s' missing: '%s'\n", + cls.ToCString(), + interface_name.ToCString(), + function_name.ToCString()); + } else if (!class_function.IsAssignableTo(interface_function)) { + // TODO(regis): Shouldn't this be IsSubtypeOf instead of IsAssignableTo? + OS::Print("%s implements '%s' with wrong result type, wrong number of " + "parameters, or wrong parameter types: '%s'\n", + cls.ToCString(), + interface_name.ToCString(), + function_name.ToCString()); + } + } + } +} +#else + +void ClassFinalizer::VerifyClassImplements(const Class& cls) {} + +#endif + + +void ClassFinalizer::VerifyBootstrapClasses() { + if (FLAG_trace_class_finalization) { + OS::Print("VerifyBootstrapClasses START.\n"); + } + ObjectStore* object_store = Isolate::Current()->object_store(); + + Class& cls = Class::Handle(); +#if defined(DEBUG) + // Basic checking. + cls = object_store->object_class(); + ASSERT(Instance::InstanceSize() == cls.instance_size()); + cls = object_store->smi_class(); + ASSERT(Smi::InstanceSize() == cls.instance_size()); + cls = object_store->one_byte_string_class(); + ASSERT(OneByteString::InstanceSize() == cls.instance_size()); + cls = object_store->two_byte_string_class(); + ASSERT(TwoByteString::InstanceSize() == cls.instance_size()); + cls = object_store->four_byte_string_class(); + ASSERT(FourByteString::InstanceSize() == cls.instance_size()); + cls = object_store->double_class(); + ASSERT(Double::InstanceSize() == cls.instance_size()); + cls = object_store->mint_class(); + ASSERT(Mint::InstanceSize() == cls.instance_size()); + cls = object_store->bigint_class(); + ASSERT(Bigint::InstanceSize() == cls.instance_size()); + cls = object_store->bool_class(); + ASSERT(Bool::InstanceSize() == cls.instance_size()); + cls = object_store->array_class(); + ASSERT(Array::InstanceSize() == cls.instance_size()); + cls = object_store->immutable_array_class(); + ASSERT(Array::InstanceSize() == cls.instance_size()); +#endif // defined(DEBUG) + + // Remember the currently pending classes. + const Array& class_array = Array::Handle(object_store->pending_classes()); + for (intptr_t i = 0; i < class_array.Length(); i++) { + // TODO(iposva): Add real checks. + cls ^= class_array.At(i); + if (cls.is_finalized() || cls.is_prefinalized()) { + // Pre-finalized bootstrap classes must not define any fields. + ASSERT(Array::Handle(cls.fields()).Length() == 0); + } + } + + // Finalize classes that aren't pre-finalized by Object::Init(). + if (!FinalizePendingClasses()) { + // TODO(srdjan): Exit like a real VM instead. + const String& err = String::Handle(object_store->sticky_error()); + OS::PrintErr("Could not verify bootstrap classes : %s\n", err.ToCString()); + OS::Exit(255); + } + if (FLAG_trace_class_finalization) { + OS::Print("VerifyBootstrapClasses END.\n"); + } + Isolate::Current()->heap()->Verify(); +} + + +// Resolve unresolved superclasses (String -> Class). +void ClassFinalizer::ResolveSuperClass(const Class& cls) { + if (cls.is_finalized()) { + return; + } + Type& super_type = Type::Handle(cls.super_type()); + if (super_type.IsNull()) { + return; + } + // Resolve failures lead to a longjmp. + super_type = ResolveType(cls, super_type); + cls.set_super_type(super_type); + const Class& super_class = Class::Handle(super_type.type_class()); + if (cls.is_interface() != super_class.is_interface()) { + String& class_name = String::Handle(cls.Name()); + String& super_class_name = String::Handle(super_class.Name()); + ReportError("class '%s' and superclass '%s' are not " + "both classes or both interfaces.\n", + class_name.ToCString(), + super_class_name.ToCString()); + } + return; +} + + +void ClassFinalizer::ResolveDefaultClass(const Class& interface) { + ASSERT(interface.is_interface()); + if (interface.is_finalized()) { + return; + } + Type& factory_type = Type::Handle(interface.factory_type()); + if (factory_type.IsNull()) { + // No resolving needed. + return; + } + // Resolve failures lead to a longjmp. + factory_type = ResolveType(interface, factory_type); + interface.set_factory_type(factory_type); + if (factory_type.IsInterfaceType()) { + const String& interface_name = String::Handle(interface.Name()); + ReportError("default clause of interface '%s' does not name a class\n", + interface_name.ToCString()); + } +} + + +RawType* ClassFinalizer::ResolveType(const Class& cls, const Type& type) { + if (type.IsResolved()) { + return type.raw(); + } + + // Resolve the type class. + if (!type.HasResolvedTypeClass()) { + const String& type_class_name = + String::Handle(type.unresolved_type_class()); + + // The type class name may be a type parameter of cls that was not resolved + // by the parser because it appeared as part of the declaration + // as T1 in B> or + // as T2 in B, T2>>. + const TypeParameter& type_parameter = TypeParameter::Handle( + cls.LookupTypeParameter(type_class_name)); + if (!type_parameter.IsNull()) { + // No need to check for proper instance scoping, since another type + // parameter must be involved for the type to still be unresolved. + // The scope checking was performed for the other type parameter already. + + // A type parameter cannot be parameterized, so report an error if type + // arguments have previously been parsed. + if (type.arguments() != TypeArguments::null()) { + ReportError("type parameter '%s' cannot be parameterized", + type_class_name.ToCString()); + } + return type_parameter.raw(); + } + + // Lookup the type class. + Class& type_class = Class::Handle(); + const Library& lib = Library::Handle(cls.library()); + ASSERT(!lib.IsNull()); + type_class = lib.LookupClass(type_class_name); + if (type_class.IsNull()) { + ReportError("cannot resolve class name '%s' from '%s'\n", + type_class_name.ToCString(), + String::Handle(cls.Name()).ToCString()); + } + // Replace unresolved type class with resolved type class. + ASSERT(type.IsParameterizedType()); + ParameterizedType& parameterized_type = ParameterizedType::Handle(); + parameterized_type ^= type.raw(); + parameterized_type.set_type_class(Object::Handle(type_class.raw())); + } + + // Resolve type arguments, if any. + const TypeArguments& arguments = TypeArguments::Handle(type.arguments()); + if (!arguments.IsNull()) { + intptr_t num_arguments = arguments.Length(); + Type& type_argument = Type::Handle(); + for (intptr_t i = 0; i < num_arguments; i++) { + type_argument = arguments.TypeAt(i); + type_argument = ResolveType(cls, type_argument); + arguments.SetTypeAt(i, type_argument); + } + } + return type.raw(); +} + + +// Finalize the type argument vector 'arguments' of the type defined by the +// class 'cls' parameterized with the type arguments 'cls_args'. +// The vector 'cls_args' is already initialized as a subvector at the correct +// position in the passed in 'arguments' vector. +// The subvector 'cls_args' has length cls.NumTypeParameters() and starts at +// offset cls.NumTypeArguments() - cls.NumTypeParameters() of the 'arguments' +// vector. +// Example: +// Declared: class C extends B { ... } +// class B extends Array { ... } +// Input: C expressed as +// cls = C, arguments = [null, null, String, double], +// i.e. cls_args = [String, double], offset = 2, length = 2. +// Output: arguments = [int, double, String, double] +void ClassFinalizer::FinalizeTypeArguments(const Class& cls, + const TypeArguments& arguments) { + ASSERT(arguments.Length() >= cls.NumTypeArguments()); + // If type checks are enabled, verify the subtyping constraints. + if (FLAG_enable_type_checks) { + const intptr_t num_type_params = cls.NumTypeParameters(); + const intptr_t offset = cls.NumTypeArguments() - num_type_params; + Type& type = Type::Handle(); + Type& type_extends = Type::Handle(); + const TypeArguments& extends_array = + TypeArguments::Handle(cls.type_parameter_extends()); + ASSERT((extends_array.IsNull() && (num_type_params == 0)) || + (extends_array.Length() == num_type_params)); + for (intptr_t i = 0; i < num_type_params; i++) { + type_extends = extends_array.TypeAt(i); + if (!type_extends.IsVarType()) { + type = arguments.TypeAt(offset + i); + if (type.IsInstantiated()) { + if (!type_extends.IsInstantiated()) { + type_extends = type_extends.InstantiateFrom(arguments, offset); + } + // TODO(regis): Where do we check the constraints when the type is + // generic? + if (!type.IsSubtypeOf(type_extends)) { + const String& type_name = String::Handle(type.Name()); + const String& extends_name = String::Handle(type_extends.Name()); + ReportError("type argument '%s' does not extend type '%s'\n", + type_name.ToCString(), + extends_name.ToCString()); + } + } + } + } + } + const Type& super_type = Type::Handle(cls.super_type()); + if (!super_type.IsNull()) { + FinalizeType(super_type); + const Class& super_class = Class::Handle(super_type.type_class()); + const TypeArguments& super_type_args = + TypeArguments::Handle(super_type.arguments()); + const intptr_t num_super_type_params = super_class.NumTypeParameters(); + const intptr_t offset = super_class.NumTypeArguments(); + const intptr_t super_offset = offset - num_super_type_params; + ASSERT(offset == (cls.NumTypeArguments() - cls.NumTypeParameters())); + Type& super_type_arg = Type::Handle(); + for (intptr_t i = 0; i < num_super_type_params; i++) { + super_type_arg = super_type_args.TypeAt(super_offset + i); + if (!super_type_arg.IsInstantiated()) { + super_type_arg = super_type_arg.InstantiateFrom(arguments, offset); + } + arguments.SetTypeAt(super_offset + i, super_type_arg); + } + FinalizeTypeArguments(super_class, arguments); + } +} + + +void ClassFinalizer::FinalizeType(const Type& type) { + ASSERT(type.IsResolved()); + if (type.IsFinalized()) { + return; + } + + // At this point, we can only have a parameterized_type. + ParameterizedType& parameterized_type = ParameterizedType::Handle(); + parameterized_type ^= type.raw(); + + if (parameterized_type.is_being_finalized()) { + ReportError("type '%s' illegally refers to itself\n", + String::Handle(parameterized_type.Name()).ToCString()); + } + + // Mark type as being finalized in order to detect illegal self reference. + parameterized_type.set_is_being_finalized(); + + // Finalize the current type arguments of the type, which are still the + // parsed type arguments. + TypeArguments& arguments = + TypeArguments::Handle(parameterized_type.arguments()); + if (!arguments.IsNull()) { + intptr_t num_arguments = arguments.Length(); + for (intptr_t i = 0; i < num_arguments; i++) { + Type& type_argument = Type::Handle(arguments.TypeAt(i)); + FinalizeType(type_argument); + } + } + + // The type class does not need to be finalized in order to finalize the type, + // however, it must at least be resolved. This was done as part of resolving + // the type itself. + Class& type_class = Class::Handle(parameterized_type.type_class()); + + // The finalized type argument vector needs num_type_arguments types. + const intptr_t num_type_arguments = type_class.NumTypeArguments(); + // The type class has num_type_parameters type parameters. + const intptr_t num_type_parameters = type_class.NumTypeParameters(); + + // Initialize the type argument vector. + // Check the number of parsed type arguments, if any. + // Specifying no type arguments indicates a raw type, which is not an error. + // However, subtyping constraints are checked below, even for a raw type. + if (!arguments.IsNull() && (arguments.Length() != num_type_parameters)) { + // TODO(regis): We need to store the token_index in each type. + ReportError("wrong number of type arguments in type '%s'\n", + String::Handle(type.Name()).ToCString()); + } + // The full type argument vector consists of the type arguments of the + // super types of type_class, which may be initialized from the parsed + // type arguments, followed by the parsed type arguments. + if (num_type_arguments > 0) { + const TypeArguments& full_arguments = TypeArguments::Handle( + TypeArguments::NewTypeArray(num_type_arguments)); + // Copy the parsed type arguments at the correct offset in the full type + // argument vector. + const intptr_t offset = num_type_arguments - num_type_parameters; + Type& type = Type::Handle(Type::VarType()); + for (intptr_t i = 0; i < num_type_parameters; i++) { + // If no type parameters were provided, a raw type is desired, so we + // create a vector of VarType. + if (!arguments.IsNull()) { + type = arguments.TypeAt(i); + } + full_arguments.SetTypeAt(offset + i, type); + } + FinalizeTypeArguments(type_class, full_arguments); + parameterized_type.set_arguments(full_arguments); + } + + // If the type is a function type, finalize the result and parameter types. + if (type_class.IsSignatureClass()) { + ResolveAndFinalizeSignature( + type_class, Function::Handle(type_class.signature_function())); + } + + parameterized_type.set_is_finalized(); +} + + +RawString* ClassFinalizer::FinalizeTypeWhileParsing(const Type& type) { + String& msg = String::Handle(); + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + LongJump* base = isolate->long_jump_base(); + LongJump jump; + isolate->set_long_jump_base(&jump); + if (setjmp(*jump.Set()) == 0) { + FinalizeType(type); + } else { + // Error occured: Get the error message. + msg = isolate->object_store()->sticky_error(); + } + isolate->set_long_jump_base(base); + return msg.raw(); +} + + +// Top level function signatures are canonicalized, added to the library class +// dictionary, and finalized with other library classes and interfaces. +// Function signatures used as type of a local variable or of a local function +// are canonicalized and finalized upon creation, since all the types they +// reference are already resolved. +void ClassFinalizer::ResolveAndFinalizeSignature(const Class& cls, + const Function& function) { + // Resolve result type. + Type& type = Type::Handle(function.result_type()); + type = ResolveType(cls, type); + function.set_result_type(type); + FinalizeType(type); + // Resolve formal parameter types. + intptr_t num_parameters = function.NumberOfParameters(); + for (intptr_t p = 0; p < num_parameters; p++) { + type = function.ParameterTypeAt(p); + type = ResolveType(cls, type); + function.SetParameterTypeAt(p, type); + FinalizeType(type); + } +} + + +static bool FuncNameExistsInSuper(const Class& cls, + const String& name) { + Class& super_class = Class::Handle(); + Function& function = Function::Handle(); + super_class = cls.SuperClass(); + while (!super_class.IsNull()) { + // Check if a field of same name exists in any super class. + function = super_class.LookupFunction(name); + if (!function.IsNull()) { + return true; + } + super_class = super_class.SuperClass(); + } + return false; +} + + +static bool FieldNameExistsInSuper(const Class& cls, const String& name) { + Class& super_class = Class::Handle(); + Field& field = Field::Handle(); + super_class = cls.SuperClass(); + while (!super_class.IsNull()) { + // Check if a function of same name exists in any super class. + field = super_class.LookupField(name); + if (!field.IsNull()) { + return true; + } + super_class = super_class.SuperClass(); + } + return false; +} + + +void ClassFinalizer::ResolveAndFinalizeMemberTypes(const Class& cls) { + // Resolve type of fields. + Array& array = Array::Handle(cls.fields()); + Field& field = Field::Handle(); + Type& type = Type::Handle(); + intptr_t num_fields = array.Length(); + String& name = String::Handle(); + for (intptr_t i = 0; i < num_fields; i++) { + field ^= array.At(i); + type = field.type(); + type = ResolveType(cls, type); + field.set_type(type); + FinalizeType(type); + name = field.name(); + if (FuncNameExistsInSuper(cls, name)) { + ReportError("field '%s' overrides a function in the super class.\n", + name.ToCString()); + } + } + // Resolve function signatures. + array = cls.functions(); + Function& function = Function::Handle(); + intptr_t num_functions = array.Length(); + String& func_name = String::Handle(); + for (intptr_t i = 0; i < num_functions; i++) { + function ^= array.At(i); + ResolveAndFinalizeSignature(cls, function); + func_name = function.name(); + if (FieldNameExistsInSuper(cls, func_name)) { + ReportError("function '%s' overrides a field in the super class.\n", + func_name.ToCString()); + } + name = Field::GetterName(func_name); + if (FuncNameExistsInSuper(cls, name)) { + ReportError("function '%s' overrides a getter in the super class.\n", + name.ToCString()); + } + name = Field::SetterName(func_name); + if (FuncNameExistsInSuper(cls, name)) { + ReportError("function '%s' overrides a setter in the super class.\n", + name.ToCString()); + } + if (function.kind() == RawFunction::kGetterFunction) { + name = String::New("get:"); + name = String::SubString(func_name, name.Length()); + if (FuncNameExistsInSuper(cls, name)) { + ReportError("'%s' overrides a function in the super class.\n", + func_name.ToCString()); + } + } + if (function.kind() == RawFunction::kSetterFunction) { + name = String::New("set:"); + name = String::SubString(func_name, name.Length()); + if (FuncNameExistsInSuper(cls, name)) { + ReportError("'%s' overrides a function in the super class.\n", + func_name.ToCString()); + } + } + } + // Resolve type of signature function. + if (cls.IsSignatureClass()) { + ResolveAndFinalizeSignature(cls, + Function::Handle(cls.signature_function())); + } +} + + +void ClassFinalizer::FinalizeClass(const Class& cls) { + if (cls.is_finalized()) { + return; + } + if (FLAG_trace_class_finalization) { + OS::Print("Finalize %s\n", cls.ToCString()); + } + if (!IsSuperCycleFree(cls)) { + const String& name = String::Handle(cls.Name()); + ReportError("class '%s' has a cycle in its superclass relationship.\n", + name.ToCString()); + } + GrowableArray visited; + ResolveInterfaces(cls, &visited); + const Type& super_type = Type::Handle(cls.super_type()); + if (!super_type.IsNull()) { + const Class& super_class = Class::Handle(super_type.type_class()); + // Finalize super class and super type. + FinalizeClass(super_class); + FinalizeType(super_type); + } + if (cls.is_interface()) { + const Type& factory_type = Type::Handle(cls.factory_type()); + if (!factory_type.IsNull()) { + const Class& factory_class = Class::Handle(factory_type.type_class()); + // Finalize factory class and factory type. + if (!factory_class.is_finalized()) { + FinalizeClass(factory_class); + // Finalizing the factory class may indirectly finalize this interface. + if (cls.is_finalized()) { + return; + } + } + FinalizeType(factory_type); + } + } + // Finalize interface types (but not necessarily interface classes). + Array& interface_types = Array::Handle(cls.interfaces()); + Type& interface_type = Type::Handle(); + for (intptr_t i = 0; i < interface_types.Length(); i++) { + interface_type ^= interface_types.At(i); + FinalizeType(interface_type); + } + // Mark as finalized before resolving member types in order to break cycles. + cls.Finalize(); + ResolveAndFinalizeMemberTypes(cls); + // Run additional checks after all types are finalized. + if (!cls.is_interface()) { + CheckForLegalOverrides(cls); + } + if (cls.is_const()) { + CheckForLegalConstClass(cls); + } +} + + +bool ClassFinalizer::IsSuperCycleFree(const Class& cls) { + Class& test1 = Class::Handle(cls.raw()); + Class& test2 = Class::Handle(cls.SuperClass()); + // A finalized class has been checked for cycles. + // Using the hare and tortoise algorithm for locating cycles. + while (!test1.is_finalized() && + !test2.IsNull() && !test2.is_finalized()) { + if (test1.raw() == test2.raw()) { + // Found a cycle. + return false; + } + test1 = test1.SuperClass(); + test2 = test2.SuperClass(); + if (!test2.IsNull()) { + test2 = test2.SuperClass(); + } + } + // No cycles. + return true; +} + + +bool ClassFinalizer::AddInterfaceIfUnique(GrowableArray* interface_list, + Type* interface, + Type* conflicting) { + String& interface_class_name = String::Handle(interface->ClassName()); + String& existing_interface_class_name = String::Handle(); + for (intptr_t i = 0; i < interface_list->length(); i++) { + existing_interface_class_name = (*interface_list)[i]->ClassName(); + if (interface_class_name.Equals(existing_interface_class_name)) { + // Same interface class name, now check names of type arguments. + const String& interface_name = String::Handle(interface->Name()); + const String& existing_interface_name = + String::Handle((*interface_list)[i]->Name()); + // TODO(regis): Revisit depending on the outcome of issue 4905685. + if (!interface_name.Equals(existing_interface_name)) { + *conflicting = (*interface_list)[i]->raw(); + return false; + } else { + return true; + } + } + } + interface_list->Add(interface); + return true; +} + + +template +static RawArray* NewArray(const GrowableArray& objs) { + Array& a = Array::Handle(Array::New(objs.length())); + for (int i = 0; i < objs.length(); i++) { + a.SetAt(i, *objs[i]); + } + return a.raw(); +} + + +// Walks the graph of explicitly declared interfaces of classes and +// interfaces recursively. Resolves unresolved interfaces. +// Returns false if there is an interface reference that cannot be +// resolved, or if there is a cycle in the graph. We detect cycles by +// remembering interfaces we've visited in each path through the +// graph. If we visit an interface a second time on a given path, +// we found a loop. +void ClassFinalizer::ResolveInterfaces(const Class& cls, + GrowableArray* visited) { + ASSERT(visited != NULL); + for (int i = 0; i < visited->length(); i++) { + if ((*visited)[i]->raw() == cls.raw()) { + // We have already visited interface class 'cls'. We found a cycle. + const String& interface_name = String::Handle(cls.Name()); + ReportError("Cyclic reference found for interface '%s'\n", + interface_name.ToCString()); + } + } + + // If the class/interface has no explicit interfaces, we are done. + Array& super_interfaces = Array::Handle(cls.interfaces()); + if (super_interfaces.Length() == 0) { + return; + } + + visited->Add(&cls); + Type& interface = Type::Handle(); + for (intptr_t i = 0; i < super_interfaces.Length(); i++) { + interface ^= super_interfaces.At(i); + interface = ResolveType(cls, interface); + super_interfaces.SetAt(i, interface); + if (interface.IsTypeParameter()) { + ReportError("Type parameter '%s' cannot be used as interface\n", + String::Handle(interface.Name()).ToCString()); + } + const Class& interface_class = Class::Handle(interface.type_class()); + if (!interface_class.is_interface()) { + ReportError("Class name '%s' used where interface expected\n", + String::Handle(interface_class.Name()).ToCString()); + } + // TODO(regis): Verify that unless cls is in core lib, it cannot implement + // an instance of Number or String. Any other? bool? + + // Now resolve the super interfaces. + ResolveInterfaces(interface_class, visited); + } + visited->RemoveLast(); +} + + +void ClassFinalizer::CheckForLegalOverrides(const Class& cls) { + HANDLESCOPE(); + const Class& super = Class::Handle(cls.SuperClass()); + if (super.IsNull()) { + return; + } + // Check functions. + // TODO(regis): It is not clear from the spec that we should be checking this. + const Array& functions_array = Array::Handle(cls.functions()); + Function& function = Function::Handle(); + String& function_name = String::Handle(); + intptr_t len = functions_array.Length(); + for (intptr_t i = 0; i < len; i++) { + function ^= functions_array.At(i); + if (!function.is_static()) { + function_name ^= function.name(); + Function& overridden_function = + Function::Handle(super.LookupDynamicFunction(function_name)); + if (!overridden_function.IsNull() && + !function.HasCompatibleParametersWith(overridden_function)) { + const String& class_name = String::Handle(cls.Name()); + ReportError("class '%s' overrides function '%s' with incompatible " + "parameters.\n", + class_name.ToCString(), function_name.ToCString()); + } + // Function types are purposely not checked for assignability. + } + } + // Check fields. + const Array& fields_array = Array::Handle(cls.fields()); + Field& field = Field::Handle(); + String& field_name = String::Handle(); + len = fields_array.Length(); + for (intptr_t i = 0; i < len; i++) { + field ^= fields_array.At(i); + field_name ^= field.name(); + Field& super_field = Field::Handle(super.LookupStaticField(field_name)); + if (super_field.IsNull()) { + super_field = super.LookupInstanceField(field_name); + } + if (!super_field.IsNull()) { + // A static field may "override" a static field. + if (!super_field.is_static() || !field.is_static()) { + const String& class_name = String::Handle(cls.Name()); + ReportError("class '%s' cannot override field '%s'.\n", + class_name.ToCString(), field_name.ToCString()); + } + } + } +} + + +// A class is marked as constant if it has one constant constructor. +// A constant class: +// - may extend only const classes. +// - has only const instance fields. +// Note: we must check for cycles before checking for const properties. +void ClassFinalizer::CheckForLegalConstClass(const Class& cls) { + ASSERT(cls.is_const()); + const Class& super = Class::Handle(cls.SuperClass()); + if (!super.IsNull() && !super.is_const()) { + String& name = String::Handle(super.Name()); + ReportError("superclass '%s' must be const.\n", name.ToCString()); + } + const Array& fields_array = Array::Handle(cls.fields()); + intptr_t len = fields_array.Length(); + Field& field = Field::Handle(); + for (intptr_t i = 0; i < len; i++) { + field ^= fields_array.At(i); + if (!field.is_static() && !field.is_final()) { + const String& class_name = String::Handle(cls.Name()); + const String& field_name = String::Handle(field.name()); + ReportError("const class '%s' has non-final field '%s'\n", + class_name.ToCString(), field_name.ToCString()); + } + } +} + + +void ClassFinalizer::PrintClassInformation(const Class& cls) { + HANDLESCOPE(); + const String& class_name = String::Handle(cls.Name()); + OS::Print("%s '%s'", + cls.is_interface() ? "interface" : "class", + class_name.ToCString()); + const Library& library = Library::Handle(cls.library()); + if (!library.IsNull()) { + OS::Print(" library '%s%s':\n", + String::Handle(library.url()).ToCString(), + String::Handle(library.private_key()).ToCString()); + } else { + OS::Print(" (null library):\n"); + } + const Array& interfaces_array = Array::Handle(cls.interfaces()); + Type& interface = Type::Handle(); + intptr_t len = interfaces_array.Length(); + for (intptr_t i = 0; i < len; i++) { + interface ^= interfaces_array.At(i); + OS::Print(" %s\n", interface.ToCString()); + } + const Array& functions_array = Array::Handle(cls.functions()); + Function& function = Function::Handle(); + len = functions_array.Length(); + for (intptr_t i = 0; i < len; i++) { + function ^= functions_array.At(i); + OS::Print(" %s\n", function.ToCString()); + } + const Array& fields_array = Array::Handle(cls.fields()); + Field& field = Field::Handle(); + len = fields_array.Length(); + for (intptr_t i = 0; i < len; i++) { + field ^= fields_array.At(i); + OS::Print(" %s\n", field.ToCString()); + } +} + + +void ClassFinalizer::ReportError(const char* format, ...) { + static const int kBufferLength = 1024; + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + Zone* zone = isolate->current_zone(); + ASSERT(zone != NULL); + char* msg_buffer = reinterpret_cast(zone->Allocate(kBufferLength + 1)); + ASSERT(msg_buffer != NULL); + va_list args; + va_start(args, format); + OS::VSNPrint(msg_buffer, kBufferLength, format, args); + va_end(args); + isolate->long_jump_base()->Jump(1, msg_buffer); + UNREACHABLE(); +} + +} // namespace dart diff --git a/runtime/vm/class_finalizer.h b/runtime/vm/class_finalizer.h new file mode 100644 index 00000000000..8c98fb532d5 --- /dev/null +++ b/runtime/vm/class_finalizer.h @@ -0,0 +1,78 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_CLASS_FINALIZER_H_ +#define VM_CLASS_FINALIZER_H_ + +#include "vm/allocation.h" +#include "vm/growable_array.h" + +namespace dart { + +class Class; +class Function; +class RawType; +class Type; +class TypeArguments; +class RawString; + +// Traverses all pending, unfinalized classes, validates and marks them as +// finalized. +class ClassFinalizer : public AllStatic { + public: + // Add 'interface' to 'interface_list' if it is not already in the list and + // return true. Also return true if 'interface' is not added, because it is + // not unique, i.e. it is already in the list. + // Return false if 'interface' conflicts with an interface already in the list + // with the same class, but different type arguments. + // In the case of a conflict, set 'conflicting' to the existing interface. + static bool AddInterfaceIfUnique(GrowableArray* interface_list, + Type* interface, + Type* conflicting); + + // Finalize type. Used for local variable types. + static void FinalizeType(const Type& type); + static RawString* FinalizeTypeWhileParsing(const Type& type); + + // Pending classes are classes that need to be finalized. + static void AddPendingClasses(const GrowableArray& classes); + + // Return false if we still have classes pending to be finalized. + static bool AllClassesFinalized(); + + // Return whether class finalization failed. + // The function returns true if the finalization was successful. + // If finalization fails, an error message is set in the sticky error field + // in the object store. + static bool FinalizePendingClasses(); + + // Verify that the pending classes have been properly prefinalized. This is + // needed during bootstrapping where the classes have been preloaded. + static void VerifyBootstrapClasses(); + + private: + static void FinalizeClass(const Class& cls); + static bool IsSuperCycleFree(const Class& cls); + static void CheckForLegalOverrides(const Class& cls); + static void CheckForLegalConstClass(const Class& cls); + static void ResolveSuperClass(const Class& cls); + static void ResolveDefaultClass(const Class& cls); + static void ResolveInterfaces(const Class& cls, + GrowableArray* visited); + static void FinalizeTypeArguments(const Class& cls, + const TypeArguments& arguments); + static RawType* ResolveType(const Class& cls, const Type& type); + static void ResolveAndFinalizeSignature(const Class& cls, + const Function& function); + static void ResolveAndFinalizeMemberTypes(const Class& cls); + static void PrintClassInformation(const Class& cls); + static void VerifyClassImplements(const Class& cls); + static void CollectInterfaces(const Class& cls, + GrowableArray* interfaces); + static void ReportError(const char* format, ...); +}; + +} // namespace dart + +#endif // VM_CLASS_FINALIZER_H_ diff --git a/runtime/vm/class_finalizer_test.cc b/runtime/vm/class_finalizer_test.cc new file mode 100644 index 00000000000..91452247a64 --- /dev/null +++ b/runtime/vm/class_finalizer_test.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/class_finalizer.h" +#include "vm/unit_test.h" + +namespace dart { + + +static RawClass* CreateTestClass(const char* name) { + const Array& empty_array = Array::Handle(Array::Empty()); + const String& class_name = String::Handle(String::NewSymbol(name)); + const Script& script = Script::Handle(); + const Class& cls = Class::Handle(Class::New(class_name, script)); + cls.set_interfaces(empty_array); + cls.SetFunctions(empty_array); + cls.SetFields(empty_array); + return cls.raw(); +} + + +TEST_CASE(ClassFinalizer) { + GrowableArray classes_1; + classes_1.Add(&Class::ZoneHandle(CreateTestClass("BMW"))); + classes_1.Add(&Class::ZoneHandle(CreateTestClass("Porsche"))); + ClassFinalizer::AddPendingClasses(classes_1); + GrowableArray classes_2; + classes_2.Add(&Class::ZoneHandle(CreateTestClass("Ferrari"))); + classes_2.Add(&Class::ZoneHandle(CreateTestClass("Fiat"))); + classes_2.Add(&Class::ZoneHandle(CreateTestClass("Alfa"))); + ClassFinalizer::AddPendingClasses(classes_2); + EXPECT(ClassFinalizer::FinalizePendingClasses()); + for (int i = 0; i < classes_1.length(); i++) { + EXPECT(classes_1[i]->is_finalized()); + } + for (int i = 0; i < classes_2.length(); i++) { + EXPECT(classes_2[i]->is_finalized()); + } + EXPECT(ClassFinalizer::FinalizePendingClasses()); +} + + +TEST_CASE(ClassFinalize_Cycles) { + GrowableArray classes; + classes.Add(&Class::ZoneHandle(CreateTestClass("Jungfrau"))); + classes.Add(&Class::ZoneHandle(CreateTestClass("Eiger"))); + // Create a cycle. + classes[0]->set_super_type( + Type::Handle(Type::NewNonParameterizedType(*classes[1]))); + classes[1]->set_super_type( + Type::Handle(Type::NewNonParameterizedType(*classes[0]))); + ClassFinalizer::AddPendingClasses(classes); + EXPECT(!ClassFinalizer::FinalizePendingClasses()); +} + + +static RawLibrary* NewLib(const char* url_chars) { + String& url = String::ZoneHandle(String::NewSymbol(url_chars)); + return Library::New(url); +} + + +TEST_CASE(ClassFinalize_Resolve) { + GrowableArray classes; + Class& rhb = Class::ZoneHandle(CreateTestClass("RhB")); + Class& sbb = Class::ZoneHandle(CreateTestClass("SBB")); + Library& lib = Library::Handle(NewLib("TestLib")); + classes.Add(&rhb); + classes.Add(&sbb); + lib.AddClass(rhb); + lib.AddClass(sbb); + const String& superclass_name = String::Handle(sbb.Name()); + TypeArguments& type_arguments = TypeArguments::Handle(); + rhb.set_super_type(Type::Handle(Type::NewParameterizedType( + Object::Handle(superclass_name.raw()), type_arguments))); + ClassFinalizer::AddPendingClasses(classes); + EXPECT(ClassFinalizer::FinalizePendingClasses()); +} + +} // namespace dart diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc new file mode 100644 index 00000000000..2b29a342579 --- /dev/null +++ b/runtime/vm/code_generator.cc @@ -0,0 +1,996 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/code_generator.h" + +#include "vm/code_index_table.h" +#include "vm/code_patcher.h" +#include "vm/compiler.h" +#include "vm/dart_entry.h" +#include "vm/exceptions.h" +#include "vm/ic_stubs.h" +#include "vm/object_store.h" +#include "vm/resolver.h" +#include "vm/runtime_entry.h" +#include "vm/stack_frame.h" +#include "vm/verifier.h" + +namespace dart { + +DEFINE_FLAG(bool, inline_cache, true, "enable inline caches"); +DEFINE_FLAG(bool, trace_deopt, false, "Trace deoptimization"); +DEFINE_FLAG(bool, trace_ic, false, "trace IC handling"); +DEFINE_FLAG(bool, trace_patching, false, "Trace patching of code."); +DEFINE_FLAG(bool, trace_runtime_calls, false, "Trace runtime calls."); + + +const Array& CodeGenerator::ArgumentsDescriptor(int num_arguments, + ArgumentListNode* arguments) { + const Array& names = arguments == NULL ? Array::Handle() : arguments->names(); + const intptr_t num_named_args = names.IsNull() ? 0 : names.Length(); + const intptr_t num_pos_args = num_arguments - num_named_args; + + // The code generator may prepend an extra positional argument to the list of + // actual parameters. This can be the receiver of an instance call, the + // allocated but uninitialized receiver of a constructor call, or the type + // argument vector of a factory call. + // The value 'num_arguments' is the actual total number of arguments, + // including the prepended positional argument, whereas + // 'arguments->length()' is the parsed number of arguments not including the + // prepended argument. + ASSERT((arguments == NULL) || + (arguments->length() == num_arguments) || + (arguments->length() == (num_arguments - 1))); + + // Build the argument descriptor array, which consists of the total number of + // arguments, the number of positional arguments, alphabetically sorted + // pairs of name/position, and a terminating null. + const int descriptor_len = 3 + (2 * num_named_args); + Array& descriptor = Array::ZoneHandle(Array::New(descriptor_len)); + + // Set total number of passed arguments. + descriptor.SetAt(0, Smi::Handle(Smi::New(num_arguments))); + // Set number of positional arguments. + descriptor.SetAt(1, Smi::Handle(Smi::New(num_pos_args))); + // Set alphabetically sorted pairs of name/position for named arguments. + String& name = String::Handle(); + Smi& pos = Smi::Handle(); + for (int i = 0; i < num_named_args; i++) { + name ^= names.At(i); + pos = Smi::New(num_pos_args + i); + int j = i; + // Shift already inserted pairs with "larger" names. + String& name_j = String::Handle(); + Smi& pos_j = Smi::Handle(); + while (--j >= 0) { + name_j ^= descriptor.At(2 + (2 * j)); + const intptr_t result = name.CompareTo(name_j); + ASSERT(result != 0); // Duplicate argument names checked in parser. + if (result > 0) break; + pos_j ^= descriptor.At(3 + (2 * j)); + descriptor.SetAt(2 + (2 * (j + 1)), name_j); + descriptor.SetAt(3 + (2 * (j + 1)), pos_j); + } + // Insert pair in descriptor array. + descriptor.SetAt(2 + (2 * (j + 1)), name); + descriptor.SetAt(3 + (2 * (j + 1)), pos); + } + // Set terminating null. + descriptor.SetAt(descriptor_len - 1, Object::Handle()); + + // Share the immutable descriptor when possible by canonicalizing it. + descriptor.MakeImmutable(); + descriptor ^= descriptor.Canonicalize(); + return descriptor; +} + + +DEFINE_RUNTIME_ENTRY(TraceFunctionEntry, 1) { + ASSERT(arguments.Count() == kTraceFunctionEntryRuntimeEntry.argument_count()); + const Function& function = Function::CheckedHandle(arguments.At(0)); + const String& function_name = String::Handle(function.name()); + const String& class_name = + String::Handle(Class::Handle(function.owner()).Name()); + OS::Print("> Entering '%s.%s'\n", + class_name.ToCString(), function_name.ToCString()); +} + + +DEFINE_RUNTIME_ENTRY(TraceFunctionExit, 1) { + ASSERT(arguments.Count() == kTraceFunctionExitRuntimeEntry.argument_count()); + const Function& function = Function::CheckedHandle(arguments.At(0)); + const String& function_name = String::Handle(function.name()); + const String& class_name = + String::Handle(Class::Handle(function.owner()).Name()); + OS::Print("< Exiting '%s.%s'\n", + class_name.ToCString(), function_name.ToCString()); +} + + +// Allocation of a fixed length array of given element type. +// Arg0: array length. +// Arg1: array element type. +// Arg2: type arguments of the instantiator. +// Return value: newly allocated array of length arg0. +DEFINE_RUNTIME_ENTRY(AllocateArray, 3) { + ASSERT(arguments.Count() == kAllocateArrayRuntimeEntry.argument_count()); + const Smi& length = Smi::CheckedHandle(arguments.At(0)); + const Array& array = Array::Handle(Array::New(length.Value())); + arguments.SetReturn(array); + TypeArguments& element_type = TypeArguments::CheckedHandle(arguments.At(1)); + if (element_type.IsNull()) { + // No instantiator required for a raw type. + ASSERT(TypeArguments::CheckedHandle(arguments.At(2)).IsNull()); + return; + } + // An Array takes only one type argument. + ASSERT(element_type.Length() == 1); + const TypeArguments& instantiator = + TypeArguments::CheckedHandle(arguments.At(2)); + if (instantiator.IsNull()) { + // Either the type element is instantiated (use it), or the instantiator is + // of a raw type and we cannot instantiate the element type (leave as null). + if (element_type.IsInstantiated()) { + array.SetTypeArguments(element_type); + } + return; + } + ASSERT(!element_type.IsInstantiated()); + // If possible, use the instantiator as the type argument vector. + if (element_type.IsUninstantiatedIdentity() && (instantiator.Length() == 1)) { + // No need to check that the instantiator is a TypeArray, since the virtual + // call to Length() handles other cases that are harder to inline. + element_type = instantiator.raw(); + } else { + element_type = TypeArguments::NewInstantiatedTypeArguments(element_type, + instantiator); + } + array.SetTypeArguments(element_type); +} + + +// Allocate a new object. +// Arg0: class of the object that needs to be allocated. +// Arg1: type arguments of the object that needs to be allocated. +// Arg2: type arguments of the instantiator. +// Return value: newly allocated object. +DEFINE_RUNTIME_ENTRY(AllocateObject, 3) { + ASSERT(arguments.Count() == kAllocateObjectRuntimeEntry.argument_count()); + const Class& cls = Class::CheckedHandle(arguments.At(0)); + const Instance& instance = Instance::Handle(Instance::New(cls)); + arguments.SetReturn(instance); + if (!cls.IsParameterized()) { + // No type arguments required for a non-parameterized type. + ASSERT(Instance::CheckedHandle(arguments.At(1)).IsNull()); + return; + } + TypeArguments& type_arguments = TypeArguments::CheckedHandle(arguments.At(1)); + if (type_arguments.IsNull()) { + // No instantiator is required for a raw type. + ASSERT(Instance::CheckedHandle(arguments.At(2)).IsNull()); + return; + } + ASSERT(type_arguments.Length() == cls.NumTypeArguments()); + const TypeArguments& instantiator = + TypeArguments::CheckedHandle(arguments.At(2)); + if (instantiator.IsNull()) { + // Either the type argument vector is instantiated (use it), or the + // instantiator is of a raw type and we cannot instantiate the type argument + // vector (leave it as null). + if (type_arguments.IsInstantiated()) { + instance.SetTypeArguments(type_arguments); + } + return; + } + ASSERT(!type_arguments.IsInstantiated()); + // If possible, use the instantiator as the type argument vector. + if (instantiator.IsTypeArray()) { + // Code inlined in the caller should have optimized the case where the + // instantiator is a TypeArray and can be used as type argument vector. + ASSERT(!type_arguments.IsUninstantiatedIdentity() || + (instantiator.Length() != type_arguments.Length())); + type_arguments = TypeArguments::NewInstantiatedTypeArguments(type_arguments, + instantiator); + } else { + if (type_arguments.IsUninstantiatedIdentity() && + (instantiator.Length() == type_arguments.Length())) { + type_arguments = instantiator.raw(); + } else { + type_arguments = + TypeArguments::NewInstantiatedTypeArguments(type_arguments, + instantiator); + } + } + instance.SetTypeArguments(type_arguments); +} + + +// Instantiate type arguments. +// Arg0: uninstantiated type arguments. +// Arg1: instantiator type arguments. +// Return value: instantiated type arguments. +DEFINE_RUNTIME_ENTRY(InstantiateTypeArguments, 2) { + ASSERT(arguments.Count() == + kInstantiateTypeArgumentsRuntimeEntry.argument_count()); + TypeArguments& type_arguments = TypeArguments::CheckedHandle(arguments.At(0)); + const TypeArguments& instantiator = + TypeArguments::CheckedHandle(arguments.At(1)); + ASSERT(!type_arguments.IsNull() && + !type_arguments.IsInstantiated() && + !instantiator.IsNull()); + // Code inlined in the caller should have optimized the case where the + // instantiator can be used as type argument vector. + ASSERT(!type_arguments.IsUninstantiatedIdentity() || + !instantiator.IsTypeArray() || + (instantiator.Length() != type_arguments.Length())); + type_arguments = TypeArguments::NewInstantiatedTypeArguments(type_arguments, + instantiator); + arguments.SetReturn(type_arguments); +} + + +// Allocate a new closure. +// Arg0: local function. +// TODO(regis): Arg1: type arguments of the closure. +// TODO(regis): Arg2: type arguments of the instantiator. +// Return value: newly allocated closure. +DEFINE_RUNTIME_ENTRY(AllocateClosure, 1) { + ASSERT(arguments.Count() == kAllocateClosureRuntimeEntry.argument_count()); + const Function& function = Function::CheckedHandle(arguments.At(0)); + // TODO(regis): Process type arguments unless the closure is static. + // The current context was saved in the Isolate structure when entering the + // runtime. + const Context& context = Context::Handle(Isolate::Current()->top_context()); + ASSERT(!context.IsNull()); + arguments.SetReturn(Closure::Handle(Closure::New(function, context))); +} + + +// Allocate a new static implicit closure. +// Arg0: local function. +// Return value: newly allocated closure. +DEFINE_RUNTIME_ENTRY(AllocateStaticImplicitClosure, 1) { + ASSERT(arguments.Count() == + kAllocateStaticImplicitClosureRuntimeEntry.argument_count()); + ObjectStore* object_store = Isolate::Current()->object_store(); + ASSERT(object_store != NULL); + const Function& function = Function::CheckedHandle(arguments.At(0)); + ASSERT(function.is_static()); // Closure functions are always static for now. + const Context& context = Context::Handle(object_store->empty_context()); + arguments.SetReturn(Closure::Handle(Closure::New(function, context))); +} + + +// Allocate a new implicit closure. +// Arg0: local function. +// Arg1: receiver object. +// TODO(regis): Arg2: type arguments of the closure. +// TODO(regis): Arg3: type arguments of the instantiator. +// Return value: newly allocated closure. +DEFINE_RUNTIME_ENTRY(AllocateImplicitClosure, 2) { + ASSERT(arguments.Count() == + kAllocateImplicitClosureRuntimeEntry.argument_count()); + const Function& function = Function::CheckedHandle(arguments.At(0)); + ASSERT(function.is_static()); // Closure functions are always static for now. + const Instance& receiver = Instance::CheckedHandle(arguments.At(1)); + Context& context = Context::Handle(); + context = Context::New(1); + context.SetAt(0, receiver); + arguments.SetReturn(Closure::Handle(Closure::New(function, context))); + // TODO(regis): Set type arguments. +} + + +// Allocate a new context large enough to hold the given number of variables. +// Arg0: number of variables. +// Return value: newly allocated context. +DEFINE_RUNTIME_ENTRY(AllocateContext, 1) { + CHECK_STACK_ALIGNMENT; + ASSERT(arguments.Count() == kAllocateContextRuntimeEntry.argument_count()); + const Smi& num_variables = Smi::CheckedHandle(arguments.At(0)); + arguments.SetReturn(Context::Handle(Context::New(num_variables.Value()))); +} + + +// Check that the given instance is an instance of the given type. +// Tested instance may not be null, because the null test is inlined. +// Arg0: instance being checked. +// Arg1: type. +// Arg2: type arguments of the instantiator of the type. +// Return value: true or false. +DEFINE_RUNTIME_ENTRY(Instanceof, 3) { + ASSERT(arguments.Count() == kInstanceofRuntimeEntry.argument_count()); + const Instance& instance = Instance::CheckedHandle(arguments.At(0)); + const Type& type = Type::CheckedHandle(arguments.At(1)); + const TypeArguments& type_instantiator = + TypeArguments::CheckedHandle(arguments.At(2)); + ASSERT(type.IsFinalized()); + ASSERT(!instance.IsNull()); + const Bool& result = Bool::Handle( + instance.IsInstanceOf(type, type_instantiator) ? + Bool::True() : Bool::False()); + arguments.SetReturn(result); +} + + +DEFINE_RUNTIME_ENTRY(Throw, 1) { + ASSERT(arguments.Count() == kThrowRuntimeEntry.argument_count()); + const Instance& exception = Instance::CheckedHandle(arguments.At(0)); + Exceptions::Throw(exception); +} + + +DEFINE_RUNTIME_ENTRY(ReThrow, 2) { + ASSERT(arguments.Count() == kReThrowRuntimeEntry.argument_count()); + const Instance& exception = Instance::CheckedHandle(arguments.At(0)); + const Instance& stacktrace = Instance::CheckedHandle(arguments.At(1)); + Exceptions::ReThrow(exception, stacktrace); +} + + +DEFINE_RUNTIME_ENTRY(PatchStaticCall, 0) { + // This function is called after successful resolving and compilation of + // the target method. + ASSERT(arguments.Count() == kPatchStaticCallRuntimeEntry.argument_count()); + DartFrameIterator iterator; + DartFrame* caller_frame = iterator.NextFrame(); + ASSERT(caller_frame != NULL); + uword target = 0; + Function& target_function = Function::Handle(); + CodePatcher::GetStaticCallAt(caller_frame->pc(), &target_function, &target); + ASSERT(target_function.HasCode()); + uword new_target = Code::Handle(target_function.code()).EntryPoint(); + // Verify that we are not patching repeatedly. + ASSERT(target != new_target); + CodePatcher::PatchStaticCallAt(caller_frame->pc(), new_target); + if (FLAG_trace_patching) { + OS::Print("PatchStaticCall: patching from 0x%x to '%s' 0x%x\n", + caller_frame->pc(), + target_function.ToFullyQualifiedCString(), + new_target); + } +} + + +// Resolves and compiles the target function of an instance call, updates +// function cache of the receiver's class and returns the compiled code or null. +// Only the number of named arguments is checked, but not the actual names. +static RawCode* ResolveCompileInstanceCallTarget(const Instance& receiver) { + DartFrameIterator iterator; + DartFrame* caller_frame = iterator.NextFrame(); + ASSERT(caller_frame != NULL); + int num_arguments = -1; + int num_named_arguments = -1; + uword target = 0; + String& function_name = String::Handle(); + CodePatcher::GetInstanceCallAt(caller_frame->pc(), + &function_name, + &num_arguments, + &num_named_arguments, + &target); + ASSERT(function_name.IsSymbol()); + Class& receiver_class = Class::Handle(); + if (receiver.IsNull()) { + // TODO(srdjan): Clarify behavior of null objects. + receiver_class = Isolate::Current()->object_store()->object_class(); + } else { + receiver_class = receiver.clazz(); + } + FunctionsCache functions_cache(receiver_class); + Code& code = Code::Handle(); + code = functions_cache.LookupCode(function_name, + num_arguments, + num_named_arguments); + if (!code.IsNull()) { + // Function's code found in the cache. + return code.raw(); + } + + Function& function = Function::Handle(); + function = Resolver::ResolveDynamic(receiver, + function_name, + num_arguments, + num_named_arguments); + if (function.IsNull()) { + return Code::null(); + } else { + if (!function.HasCode()) { + Compiler::CompileFunction(function); + } + functions_cache.AddCompiledFunction(function, + num_arguments, + num_named_arguments); + return function.code(); + } +} + + +// Result of an invoke may be an unhandled exception, in which case we +// rethrow it. +static void CheckResultException(const Instance& result) { + if (result.IsUnhandledException()) { + const UnhandledException& unhandled = UnhandledException::Handle( + reinterpret_cast(result.raw())); + const Instance& excp = Instance::Handle(unhandled.exception()); + const Instance& stack = Instance::Handle(unhandled.stacktrace()); + Exceptions::ReThrow(excp, stack); + } +} + + +// Resolves an instance function and compiles it if necessary. +// Arg0: receiver object. +// Returns: RawCode object or NULL (method not found or not compileable). +// This is called by the megamorphic stub when instance call does not need to be +// patched. +DEFINE_RUNTIME_ENTRY(ResolveCompileInstanceFunction, 1) { + ASSERT(arguments.Count() == + kResolveCompileInstanceFunctionRuntimeEntry.argument_count()); + const Instance& receiver = Instance::CheckedHandle(arguments.At(0)); + const Code& code = Code::Handle(ResolveCompileInstanceCallTarget(receiver)); + arguments.SetReturn(Code::Handle(code.raw())); +} + + +// Resolve instance call and patch it to jump to IC stub or megamorphic stub. +// After patching the caller's instance call instruction, that call will +// be reexecuted and ran through the created IC stub. The null receivers +// have special handling, i.e., they lead to megamorphic lookup that implements +// the appropriate null behavior. +// Arg0: receiver object. +DEFINE_RUNTIME_ENTRY(ResolvePatchInstanceCall, 1) { + ASSERT(arguments.Count() == + kResolvePatchInstanceCallRuntimeEntry.argument_count()); + const Instance& receiver = Instance::CheckedHandle(arguments.At(0)); + const Code& code = Code::Handle(ResolveCompileInstanceCallTarget(receiver)); + DartFrameIterator iterator; + DartFrame* caller_frame = iterator.NextFrame(); + String& function_name = String::Handle(); + if ((!receiver.IsNull() && code.IsNull()) || !FLAG_inline_cache) { + // Let megamorphic lookup handle noSuchMethod. + CodePatcher::PatchInstanceCallAt( + caller_frame->pc(), StubCode::MegamorphicLookupEntryPoint()); + if (FLAG_trace_ic) { + OS::Print("IC: cannot find function at 0x%x -> megamorphic lookup.\n", + caller_frame->pc()); + } + if (FLAG_trace_patching) { + OS::Print("ResolvePatchInstanceCall: patching 0x%x to megamorphic\n", + caller_frame->pc()); + } + } else { + int num_arguments = -1; + int num_named_arguments = -1; + uword caller_target = 0; + CodePatcher::GetInstanceCallAt(caller_frame->pc(), + &function_name, + &num_arguments, + &num_named_arguments, + &caller_target); + // If caller_target is not in CallInstanceFunction stub (resolve call) + // then it must be pointing to an IC stub. + const Class& receiver_class = Class::ZoneHandle(receiver.clazz()); + const bool ic_miss = + !StubCode::InCallInstanceFunctionStubCode(caller_target); + GrowableArray classes; + GrowableArray targets; + if (ic_miss) { + bool is_ic = + ICStubs::RecognizeICStub(caller_target, &classes, &targets); + ASSERT(is_ic); + ASSERT(classes.length() == targets.length()); + // The returned classes array can be empty if the first patch occured + // with a null class. 'receiver_class' should not exists. + ASSERT(ICStubs::IndexOfClass(classes, receiver_class) < 0); + ASSERT(!code.IsNull()); + ASSERT(!receiver_class.IsNullClass()); + const Function& function = Function::ZoneHandle(code.function()); + targets.Add(&function); + classes.Add(&receiver_class); + } else { + // First patch of instance call. + // Do not add classes for null receiver. For first IC patch it means that + // the IC will always miss and jump to megamorphic lookup (null handling). + if (!receiver_class.IsNullClass()) { + ASSERT(!code.IsNull()); + const Function& function = Function::ZoneHandle(code.function()); + targets.Add(&function); + classes.Add(&receiver_class); + } + } + const Code& ic_code = Code::Handle(ICStubs::GetICStub(classes, targets)); + if (FLAG_trace_ic) { + CodeIndexTable* ci_table = Isolate::Current()->code_index_table(); + ASSERT(ci_table != NULL); + const Function& caller = + Function::Handle(ci_table->LookupFunction(caller_frame->pc())); + const char* patch_kind = ic_miss ? "miss" : "patch"; + OS::Print("IC %s at 0x%x '%s' (receiver:'%s' function:'%s')", + patch_kind, + caller_frame->pc(), + String::Handle(caller.name()).ToCString(), + receiver.ToCString(), + function_name.ToCString()); + OS::Print(" patched to 0x%x\n", ic_code.EntryPoint()); + if (ic_miss) { + for (int i = 0; i < classes.length(); i++) { + OS::Print(" IC Miss on %s\n", classes[i]->ToCString()); + } + } + } + CodePatcher::PatchInstanceCallAt(caller_frame->pc(), ic_code.EntryPoint()); + if (FLAG_trace_patching) { + OS::Print("ResolvePatchInstanceCall: patching 0x%x to ic 0x%x\n", + caller_frame->pc(), ic_code.EntryPoint()); + } + } +} + + +static RawFunction* LookupDynamicFunction(Class* cls, const String& name) { + Function& function = Function::Handle(); + while (!cls->IsNull()) { + // Check if function exists. + function = cls->LookupDynamicFunction(name); + if (!function.IsNull()) { + break; + } + *cls = cls->SuperClass(); + } + return function.raw(); +} + + +// Resolve an implicit closure by checking if an instance function +// of the same name exists and creating a closure object of the function. +// Arg0: receiver object. +// Arg1: original function name. +// Returns: Closure object or NULL (instance function not found). +// This is called by the megamorphic stub when it is unable to resolve an +// instance method. This is done just before the call to noSuchMethod. +DEFINE_RUNTIME_ENTRY(ResolveImplicitClosureFunction, 2) { + ASSERT(arguments.Count() == + kResolveImplicitClosureFunctionRuntimeEntry.argument_count()); + const Instance& receiver = Instance::CheckedHandle(arguments.At(0)); + const String& original_func_name = String::CheckedHandle(arguments.At(1)); + if (receiver.IsNull()) { + // No implicit closure functions on null. + GrowableArray args; + Exceptions::ThrowByType(Exceptions::kNullPointer, args); + } + const String& getter_prefix = String::Handle(String::New("get:")); + Closure& closure = Closure::Handle(); + if (!original_func_name.StartsWith(getter_prefix)) { + // This is not a getter so can't be the case where we are trying to + // create an implicit closure of an instance function. + arguments.SetReturn(closure); + return; + } + Class& receiver_class = Class::Handle(); + receiver_class ^= receiver.clazz(); + ASSERT(!receiver_class.IsNull()); + String& func_name = String::Handle(); + func_name = String::SubString(original_func_name, getter_prefix.Length()); + func_name = String::NewSymbol(func_name); + const Function& function = + Function::Handle(LookupDynamicFunction(&receiver_class, func_name)); + if (function.IsNull()) { + // There is no function of the same name so can't be the case where + // we are trying to create an implicit closure of an instance function. + arguments.SetReturn(closure); + return; + } + Function& implicit_closure_function = + Function::Handle(function.ImplicitClosureFunction()); + // Create a closure object for the implicit closure function. + const Context& context = Context::Handle(Context::New(1)); + context.SetAt(0, receiver); + closure = Closure::New(implicit_closure_function, context); + arguments.SetReturn(closure); +} + + +// Resolve an implicit closure by invoking getter and checking if the return +// value from getter is a closure. +// Arg0: receiver object. +// Arg1: original function name. +// Returns: Closure object or NULL (closure not found). +// This is called by the megamorphic stub when it is unable to resolve an +// instance method. This is done just before the call to noSuchMethod. +DEFINE_RUNTIME_ENTRY(ResolveImplicitClosureThroughGetter, 2) { + ASSERT(arguments.Count() == + kResolveImplicitClosureThroughGetterRuntimeEntry.argument_count()); + const Instance& receiver = Instance::CheckedHandle(arguments.At(0)); + const String& original_function_name = String::CheckedHandle(arguments.At(1)); + const int kNumArguments = 1; + const int kNumNamedArguments = 0; + const String& getter_function_name = + String::Handle(Field::GetterName(original_function_name)); + Function& function = Function::ZoneHandle( + Resolver::ResolveDynamic(receiver, + getter_function_name, + kNumArguments, + kNumNamedArguments)); + Code& code = Code::Handle(); + if (function.IsNull()) { + arguments.SetReturn(code); + return; // No getter function found so can't be an implicit closure. + } + GrowableArray invoke_arguments(0); + const Instance& result = + Instance::Handle( + DartEntry::InvokeDynamic(receiver, function, invoke_arguments)); + if (result.IsUnhandledException()) { + arguments.SetReturn(code); + return; // Error accessing getter, treat as no such method. + } + if (!result.IsSmi()) { + const Class& cls = Class::Handle(result.clazz()); + ASSERT(!cls.IsNull()); + function = cls.signature_function(); + if (!function.IsNull()) { + arguments.SetReturn(result); + return; // Return closure object. + } + } + Exceptions::ThrowByType(Exceptions::kObjectNotClosure, invoke_arguments); +} + + +// Invoke Implicit Closure function. +// Arg0: closure object. +// Arg1: arguments descriptor (originally passed as dart instance invocation). +// Arg2: arguments array (originally passed to dart instance invocation). +DEFINE_RUNTIME_ENTRY(InvokeImplicitClosureFunction, 3) { + ASSERT(arguments.Count() == + kInvokeImplicitClosureFunctionRuntimeEntry.argument_count()); + const Closure& closure = Closure::CheckedHandle(arguments.At(0)); + const Array& arg_descriptor = Array::CheckedHandle(arguments.At(1)); + const Array& func_arguments = Array::CheckedHandle(arguments.At(2)); + const Function& function = Function::Handle(closure.function()); + ASSERT(!function.IsNull()); + if (!function.HasCode()) { + Compiler::CompileFunction(function); + } + const Context& context = Context::Handle(closure.context()); + const Code& code = Code::Handle(function.code()); + ASSERT(!code.IsNull()); + const Instructions& instrs = Instructions::Handle(code.instructions()); + ASSERT(!instrs.IsNull()); + + // Adjust arguments descriptor array to account for removal of the receiver + // parameter. Since the arguments descriptor array is canonicalized, create a + // new one instead of patching the original one. + const intptr_t len = arg_descriptor.Length(); + const intptr_t num_named_args = (len - 3) / 2; + const Array& adjusted_arg_descriptor = Array::Handle(Array::New(len)); + Smi& smi = Smi::Handle(); + smi ^= arg_descriptor.At(0); // Get argument length. + smi = Smi::New(smi.Value() - 1); // Adjust argument length. + ASSERT(smi.Value() == func_arguments.Length()); + adjusted_arg_descriptor.SetAt(0, smi); + smi ^= arg_descriptor.At(1); // Get number of positional parameters. + smi = Smi::New(smi.Value() - 1); // Adjust number of positional params. + adjusted_arg_descriptor.SetAt(1, smi); + // Adjust name/position pairs for each named argument. + String& named_arg_name = String::Handle(); + Smi& named_arg_pos = Smi::Handle(); + for (intptr_t i = 0; i < num_named_args; i++) { + const int index = 2 + (2 * i); + named_arg_name ^= arg_descriptor.At(index); + ASSERT(named_arg_name.IsSymbol()); + adjusted_arg_descriptor.SetAt(index, named_arg_name); + named_arg_pos ^= arg_descriptor.At(index + 1); + named_arg_pos = Smi::New(named_arg_pos.Value() - 1); + adjusted_arg_descriptor.SetAt(index + 1, named_arg_pos); + } + adjusted_arg_descriptor.SetAt(len - 1, Object::Handle(Object::null())); + // It is too late to share the descriptor by canonicalizing it. However, it is + // important that the argument names are canonicalized (i.e. are symbols). + + // Receiver parameter has already been skipped by caller. + GrowableArray invoke_arguments(0); + for (intptr_t i = 0; i < func_arguments.Length(); i++) { + const Object& value = Object::Handle(func_arguments.At(i)); + invoke_arguments.Add(&value); + } + + // Now Call the invoke stub which will invoke the closure. + DartEntry::invokestub entrypoint = reinterpret_cast( + StubCode::InvokeDartCodeEntryPoint()); + ASSERT(context.isolate() == Isolate::Current()); + const Instance& result = Instance::Handle( + entrypoint(instrs.EntryPoint(), + adjusted_arg_descriptor, + invoke_arguments.data(), + context)); + CheckResultException(result); + arguments.SetReturn(result); +} + + +// Invoke appropriate noSuchMethod function. +// Arg0: receiver. +// Arg1: original function name. +// Arg2: original arguments descriptor array. +// Arg3: original arguments array. +DEFINE_RUNTIME_ENTRY(InvokeNoSuchMethodFunction, 4) { + ASSERT(arguments.Count() == + kInvokeNoSuchMethodFunctionRuntimeEntry.argument_count()); + const Instance& receiver = Instance::CheckedHandle(arguments.At(0)); + const String& original_function_name = String::CheckedHandle(arguments.At(1)); + ASSERT(!Array::CheckedHandle(arguments.At(2)).IsNull()); + const Array& orig_arguments = Array::CheckedHandle(arguments.At(3)); + // TODO(regis): The signature of the "noSuchMethod" method has to change from + // noSuchMethod(String name, Array arguments) to something like + // noSuchMethod(InvocationMirror call). + const int kNumArguments = 3; + const int kNumNamedArguments = 0; + const String& function_name = + String::Handle(String::NewSymbol("noSuchMethod")); + const Function& function = Function::ZoneHandle( + Resolver::ResolveDynamic(receiver, + function_name, + kNumArguments, + kNumNamedArguments)); + ASSERT(!function.IsNull()); + GrowableArray invoke_arguments(2); + invoke_arguments.Add(&original_function_name); + invoke_arguments.Add(&orig_arguments); + const Instance& result = + Instance::Handle( + DartEntry::InvokeDynamic(receiver, function, invoke_arguments)); + CheckResultException(result); + arguments.SetReturn(result); +} + + +// Report that an object is not a closure. +// Arg0: non-closure object. +// Arg1: arguments array. +DEFINE_RUNTIME_ENTRY(ReportObjectNotClosure, 2) { + ASSERT(arguments.Count() == + kReportObjectNotClosureRuntimeEntry.argument_count()); + const Instance& bad_closure = Instance::CheckedHandle(arguments.At(0)); + // const Array& arguments = Array::CheckedHandle(arguments.At(1)); + OS::PrintErr("object '%s' is not a closure\n", bad_closure.ToCString()); + GrowableArray args; + Exceptions::ThrowByType(Exceptions::kObjectNotClosure, args); +} + + +DEFINE_RUNTIME_ENTRY(ClosureArgumentMismatch, 0) { + ASSERT(arguments.Count() == + kClosureArgumentMismatchRuntimeEntry.argument_count()); + GrowableArray args; + Exceptions::ThrowByType(Exceptions::kClosureArgumentMismatch, args); +} + + +DEFINE_RUNTIME_ENTRY(StackOverflow, 0) { + ASSERT(arguments.Count() == + kStackOverflowRuntimeEntry.argument_count()); + uword old_stack_limit = Isolate::Current()->stack_limit(); + Isolate::Current()->AdjustStackLimitForException(); + // Recursive stack overflow check. + ASSERT(old_stack_limit != Isolate::Current()->stack_limit()); + GrowableArray args; + Exceptions::ThrowByType(Exceptions::kStackOverflow, args); + Isolate::Current()->ResetStackLimitAfterException(); +} + + +static void DisableOldCode(const Function& function, + const Code& old_code, + const Code& new_code) { + const Array& class_ic_stubs = Array::Handle(old_code.class_ic_stubs()); + if (function.IsClosureFunction()) { + // Nothing to do, code may not have inline caches. + ASSERT(class_ic_stubs.Length() == 0); + return; + } + if (function.is_static() || function.IsConstructor()) { + ASSERT(class_ic_stubs.Length() == 0); + return; + } + Code& ic_stub = Code::Handle(); + for (int i = 0; i < class_ic_stubs.Length(); i += 2) { + // i: array of classes, i + 1: ic stub code. + ic_stub ^= class_ic_stubs.At(i + 1); + ICStubs::PatchTargets(ic_stub.EntryPoint(), + old_code.EntryPoint(), + new_code.EntryPoint()); + } + new_code.set_class_ic_stubs(class_ic_stubs); + old_code.set_class_ic_stubs(Array::Handle(Array::Empty())); +} + + +// Only unoptimized code has invocation counter threshold checking. +// Once the invocation counter threshold is reached any entry into the +// unoptimized code is redirected to this function. +DEFINE_RUNTIME_ENTRY(OptimizeInvokedFunction, 1) { + ASSERT(arguments.Count() == + kOptimizeInvokedFunctionRuntimeEntry.argument_count()); + const Function& function = Function::CheckedHandle(arguments.At(0)); + ASSERT(function.is_optimizable()); + ASSERT(!Code::Handle(function.code()).is_optimized()); + const Code& unoptimized_code = Code::Handle(function.code()); + // Compilation patches the entry of unoptimized code. + Compiler::CompileOptimizedFunction(function); + const Code& optimized_code = Code::Handle(function.code()); + ASSERT(!optimized_code.IsNull()); + ASSERT(!unoptimized_code.IsNull()); + DisableOldCode(function, unoptimized_code, optimized_code); +} + + +// The caller must be a static call in a Dart frame, or an entry frame. +// Patch static call to point to 'new_entry_point'. +DEFINE_RUNTIME_ENTRY(FixCallersTarget, 1) { + ASSERT(arguments.Count() == kFixCallersTargetRuntimeEntry.argument_count()); + const Function& function = Function::CheckedHandle(arguments.At(0)); + ASSERT(!function.IsNull()); + ASSERT(function.HasCode()); + + StackFrameIterator iterator(StackFrameIterator::kDontValidateFrames); + StackFrame* frame = iterator.NextFrame(); + while (frame != NULL && !frame->IsDartFrame() && !frame->IsEntryFrame()) { + frame = iterator.NextFrame(); + } + ASSERT(frame != NULL); + if (frame->IsDartFrame()) { + uword target = 0; + Function& target_function = Function::Handle(); + CodePatcher::GetStaticCallAt(frame->pc(), &target_function, &target); + const uword new_entry_point = Code::Handle(function.code()).EntryPoint(); + ASSERT(target != new_entry_point); // Why patch otherwise. + ASSERT(target_function.HasCode()); + CodePatcher::PatchStaticCallAt(frame->pc(), new_entry_point); + if (FLAG_trace_patching) { + OS::Print("FixCallersTarget: patching from 0x%x to '%s' 0x%x\n", + frame->pc(), + target_function.ToFullyQualifiedCString(), + new_entry_point); + } + } +} + + +// The top Dart frame belongs to the optimized method that needs to be +// deoptimized. The pc of the Dart frame points to the deoptimization point. +// Find the node id of the deoptimization point and find the continuation +// pc in the unoptimized code. +// Since both unoptimized and optimized code have the same layout, we need only +// to patch the pc of the Dart frame and to disable/enable appropriate code. +DEFINE_RUNTIME_ENTRY(Deoptimize, 0) { + ASSERT(arguments.Count() == kDeoptimizeRuntimeEntry.argument_count()); + DartFrameIterator iterator; + DartFrame* caller_frame = iterator.NextFrame(); + ASSERT(caller_frame != NULL); + CodeIndexTable* ci_table = Isolate::Current()->code_index_table(); + const Code& optimized_code = + Code::Handle(ci_table->LookupCode(caller_frame->pc())); + const Function& function = Function::Handle(optimized_code.function()); + ASSERT(!function.IsNull()); + const Code& unoptimized_code = Code::Handle(function.unoptimized_code()); + ASSERT(!optimized_code.IsNull() && optimized_code.is_optimized()); + ASSERT(!unoptimized_code.IsNull() && !unoptimized_code.is_optimized()); + const PcDescriptors& descriptors = + PcDescriptors::Handle(optimized_code.pc_descriptors()); + ASSERT(!descriptors.IsNull()); + // Locate node id at deoptimization point inside optimized code. + intptr_t deopt_node_id = AstNode::kInvalidId; + for (int i = 0; i < descriptors.Length(); i++) { + if (static_cast(descriptors.PC(i)) == caller_frame->pc()) { + deopt_node_id = descriptors.NodeId(i); + break; + } + } + ASSERT(deopt_node_id != AstNode::kInvalidId); + uword continue_at_pc = + unoptimized_code.GetDeoptPcAtNodeId(deopt_node_id); + ASSERT(continue_at_pc != 0); + if (FLAG_trace_deopt) { + OS::Print("Deoptimizing at pc 0x%x id %d '%s' -> continue at 0x%x \n", + caller_frame->pc(), deopt_node_id, function.ToFullyQualifiedCString(), + continue_at_pc); + } + caller_frame->set_pc(continue_at_pc); + // Clear invocation counter so that the function gets optimized after + // types/classes have been collected. + function.set_invocation_counter(0); + function.set_deoptimization_counter(function.deoptimization_counter() + 1); + + // Get unoptimized code. Compilation restores (reenables) the entry of + // unoptimized code. + Compiler::CompileFunction(function); + + DisableOldCode(function, optimized_code, unoptimized_code); + if (FLAG_trace_deopt) { + OS::Print("After patching ->0x%x:\n", continue_at_pc); + } +} + + +// We are entering function name for a valid argument count. +void FunctionsCache::EnterFunctionAt(int i, + const Array& cache, + const Function& function, + int num_arguments, + int num_named_arguments) { + ASSERT((i % kNumEntries) == 0); + ASSERT(function.AreValidArgumentCounts(num_arguments, num_named_arguments)); + cache.SetAt(i + FunctionsCache::kFunctionName, + String::Handle(function.name())); + cache.SetAt(i + FunctionsCache::kArgCount, + Smi::Handle(Smi::New(num_arguments))); + cache.SetAt(i + FunctionsCache::kNamedArgCount, + Smi::Handle(Smi::New(num_named_arguments))); + cache.SetAt(i + FunctionsCache::kFunction, function); +} + + +void FunctionsCache::AddCompiledFunction(const Function& function, + int num_arguments, + int num_named_arguments) { + ASSERT(function.HasCode()); + Array& cache = Array::Handle(class_.functions_cache()); + // Search for first free slot. Last entry is always NULL object. + for (intptr_t i = 0; i < (cache.Length() - kNumEntries); i += kNumEntries) { + if (Object::Handle(cache.At(i)).IsNull()) { + EnterFunctionAt(i, + cache, + function, + num_arguments, + num_named_arguments); + return; + } + } + intptr_t ix = cache.Length() - kNumEntries; + // Grow by 8 entries. + cache = Array::Grow(cache, cache.Length() + (8 * kNumEntries)); + class_.set_functions_cache(cache); + EnterFunctionAt(ix, + cache, + function, + num_arguments, + num_named_arguments); +} + + +// TODO(regis): The actual names of named arguments must match as well. +RawCode* FunctionsCache::LookupCode(const String& function_name, + int num_arguments, + int num_named_arguments) { + const Array& cache = Array::Handle(class_.functions_cache()); + String& test_name = String::Handle(); + for (intptr_t i = 0; i < cache.Length(); i += kNumEntries) { + test_name ^= cache.At(i + FunctionsCache::kFunctionName); + if (test_name.IsNull()) { + // Found NULL, no more entries to check, abort lookup. + return Code::null(); + } + if (function_name.Equals(test_name)) { + Smi& smi = Smi::Handle(); + smi ^= cache.At(i + FunctionsCache::kArgCount); + if (num_arguments == smi.Value()) { + smi ^= cache.At(i + FunctionsCache::kNamedArgCount); + if (num_named_arguments == smi.Value()) { + Function& result = Function::Handle(); + result ^= cache.At(i + FunctionsCache::kFunction); + ASSERT(!result.IsNull()); + ASSERT(result.HasCode()); + return result.code(); + } + } + } + } + // The cache is null terminated, therefore the loop above should never + // terminate by itself. + UNREACHABLE(); + return Code::null(); +} + +} // namespace dart diff --git a/runtime/vm/code_generator.h b/runtime/vm/code_generator.h new file mode 100644 index 00000000000..6667a2e9109 --- /dev/null +++ b/runtime/vm/code_generator.h @@ -0,0 +1,93 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_CODE_GENERATOR_H_ +#define VM_CODE_GENERATOR_H_ + +#include "vm/globals.h" +#include "vm/runtime_entry.h" + +#if defined(TARGET_ARCH_IA32) +#include "vm/code_generator_ia32.h" +#elif defined(TARGET_ARCH_X64) +#include "vm/code_generator_x64.h" +#elif defined(TARGET_ARCH_ARM) +#include "vm/code_generator_arm.h" +#else +#error Unknown architecture. +#endif + +namespace dart { + +// Declaration of runtime entries called from stub or generated code. +DECLARE_RUNTIME_ENTRY(AllocateArray); +DECLARE_RUNTIME_ENTRY(AllocateClosure); +DECLARE_RUNTIME_ENTRY(AllocateImplicitClosure); +DECLARE_RUNTIME_ENTRY(AllocateStaticImplicitClosure); +DECLARE_RUNTIME_ENTRY(AllocateContext); +DECLARE_RUNTIME_ENTRY(AllocateObject); +DECLARE_RUNTIME_ENTRY(ClosureArgumentMismatch); +DECLARE_RUNTIME_ENTRY(Deoptimize); +DECLARE_RUNTIME_ENTRY(FixCallersTarget); +DECLARE_RUNTIME_ENTRY(Instanceof); +DECLARE_RUNTIME_ENTRY(InstantiateTypeArguments); +DECLARE_RUNTIME_ENTRY(InvokeImplicitClosureFunction); +DECLARE_RUNTIME_ENTRY(InvokeNoSuchMethodFunction); +DECLARE_RUNTIME_ENTRY(OptimizeInvokedFunction); +DECLARE_RUNTIME_ENTRY(PatchStaticCall); +DECLARE_RUNTIME_ENTRY(ReportObjectNotClosure); +DECLARE_RUNTIME_ENTRY(ResolveCompileInstanceFunction); +DECLARE_RUNTIME_ENTRY(ResolvePatchInstanceCall); +DECLARE_RUNTIME_ENTRY(ResolveImplicitClosureFunction); +DECLARE_RUNTIME_ENTRY(ResolveImplicitClosureThroughGetter); +DECLARE_RUNTIME_ENTRY(ReThrow); +DECLARE_RUNTIME_ENTRY(StackOverflow); +DECLARE_RUNTIME_ENTRY(Throw); +DECLARE_RUNTIME_ENTRY(TraceFunctionEntry); +DECLARE_RUNTIME_ENTRY(TraceFunctionExit); + + +// This class wraps around the array RawClass::functions_cache_. +// The structure of that array is specified by FunctionsCache::Entries. +// The last entry in the array is always NULL, the lookup code can rely on +// last entry being NULL to terminate its search. +// The compiled functions are added sequentially (except for the last entry +// being empty). Names of functions with variable number of arguments +// can appear several times, once for each valid argument count. +class FunctionsCache : public ValueObject { + public: + // Entries in the RawClass::functions_cache_. The size of initially allocated + // functions_cache_ array is (kInitialSize * kNumEntries). + enum Entries { + kFunctionName = 0, + kArgCount = 1, + kNamedArgCount = 2, + kFunction = 3, + kNumEntries = 4 + }; + + explicit FunctionsCache(const Class& cls) : class_(cls) {} + + void AddCompiledFunction(const Function& function, + int num_arguments, + int num_named_arguments); + + // This is a testing function, the lookup (will) occur inlined in stub code. + RawCode* LookupCode(const String& function_name, + int num_arguments, + int num_named_arguments); + + private: + static void EnterFunctionAt(int i, + const Array& cache, + const Function& function, + int num_arguments, + int num_named_arguments); + + const Class& class_; +}; + +} // namespace dart + +#endif // VM_CODE_GENERATOR_H_ diff --git a/runtime/vm/code_generator_arm.h b/runtime/vm/code_generator_arm.h new file mode 100644 index 00000000000..a6dd5e3e5aa --- /dev/null +++ b/runtime/vm/code_generator_arm.h @@ -0,0 +1,50 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_CODE_GENERATOR_ARM_H_ +#define VM_CODE_GENERATOR_ARM_H_ + +#ifndef VM_CODE_GENERATOR_H_ +#error Do not include code_generator_arm.h directly; use assembler.h instead. +#endif + +#include "vm/allocation.h" +#include "vm/ast.h" +#include "vm/growable_array.h" + +namespace dart { + +// Forward declarations. +class Assembler; +class AstNode; +class ParsedFunction; + +class CodeGenerator : public AstNodeVisitor { + public: + CodeGenerator(Assembler* assembler, const ParsedFunction& parsed_function) { } + virtual ~CodeGenerator() { } + + bool GenerateCode() { + return false; + } + + // Add exception handler table to code. + void FinalizeExceptionHandlers(const Code& code) { UNIMPLEMENTED(); } + + // Add pc descriptors to code. + void FinalizePcDescriptors(const Code& code) { UNIMPLEMENTED(); } + + // Allocate and return an arguments descriptor for the given 'num_arguments' + // and 'arguments'. If 'arguments' is NULL, treat all 'num_arguments' as + // positional arguments. + static const Array& ArgumentsDescriptor(int num_arguments, + ArgumentListNode* arguments); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(CodeGenerator); +}; + +} // namespace dart + +#endif // VM_CODE_GENERATOR_ARM_H_ diff --git a/runtime/vm/code_generator_ia32.cc b/runtime/vm/code_generator_ia32.cc new file mode 100644 index 00000000000..7aa36fbca94 --- /dev/null +++ b/runtime/vm/code_generator_ia32.cc @@ -0,0 +1,2578 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. +#if defined(TARGET_ARCH_IA32) + +#include "vm/code_generator.h" + +#include "lib/error.h" +#include "vm/ast_printer.h" +#include "vm/dart_entry.h" +#include "vm/longjump.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/parser.h" +#include "vm/resolver.h" +#include "vm/stub_code.h" + +namespace dart { + +DEFINE_FLAG(bool, print_ast, false, "Print abstract syntax tree."); +DEFINE_FLAG(bool, print_scopes, false, "Print scopes of local variables."); +DEFINE_FLAG(bool, trace_functions, false, "Trace entry of each function."); +DEFINE_FLAG(int, optimization_invocation_threshold, 1000, + "number of invocations before a fucntion is optimized, -1 means never."); +DECLARE_FLAG(bool, enable_type_checks); +DECLARE_FLAG(bool, report_invocation_count); +DECLARE_FLAG(bool, trace_compiler); + +#define __ assembler_-> + + +class CodeGeneratorState : public StackResource { + public: + explicit CodeGeneratorState(CodeGenerator* codegen) + : codegen_(codegen), parent_(codegen->state()) { + if (parent_ != NULL) { + root_node_ = parent_->root_node_; + loop_level_ = parent_->loop_level_; + context_level_ = parent_->context_level_; + current_try_index_ = parent_->current_try_index_; + } else { + root_node_ = NULL; + loop_level_ = 0; + context_level_ = 0; + current_try_index_ = CatchClauseNode::kInvalidTryIndex; + } + codegen_->set_state(this); + } + virtual ~CodeGeneratorState() { + codegen_->set_state(parent_); + } + + CodeGeneratorState* parent() const { return parent_; } + + AstNode* root_node() const { return root_node_; } + void set_root_node(AstNode* value) { root_node_ = value; } + bool IsRootNode(AstNode* node) const { + return root_node_ == node; + } + + int loop_level() const { return loop_level_; } + void set_loop_level(int loop_level) { + loop_level_ = loop_level; + } + + int context_level() const { return context_level_; } + void set_context_level(int context_level) { + context_level_ = context_level; + } + + int try_index() const { return current_try_index_; } + void set_try_index(int value) { + current_try_index_ = value; + } + + private: + CodeGenerator* codegen_; + CodeGeneratorState* parent_; + AstNode* root_node_; + + // The loop level reflects the lexical nesting of loop statements, regardless + // of the presence of captured variables. + int loop_level_; + + // The runtime context level is only incremented when a new context is + // allocated and chained to the list of contexts. This occurs when the scopes + // at the current loop level contain captured variables. + int context_level_; + + // We identify each try block in this function with an unique 'try index' + // value. + // The 'try index' is used to match the try blocks with the corresponding + // catch block (if one exists). The PC descriptors generated for + // statements in the try block use this index so that it can be matched + // to the appropriate catch block. + // The 'try index' value is generated by incrementing the try_index_ + // variable in the CodeGenerator object. + // We store the 'try index' of the block of code that we are + // currently generating code for in the current_try_index_ variable. + int current_try_index_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(CodeGeneratorState); +}; + + +class CodeGenerator::DescriptorList : public ZoneAllocated { + public: + struct PcDesc { + intptr_t pc_offset; // PC offset value of the descriptor. + PcDescriptors::Kind kind; // Descriptor kind (kDeopt, kOther). + intptr_t node_id; // AST node id. + intptr_t token_index; // Token position in source of PC. + intptr_t try_index; // Try block index of PC. + }; + + DescriptorList() : list_() { + } + ~DescriptorList() { } + + intptr_t Length() const { + return list_.length(); + } + + intptr_t PcOffset(int index) const { + return list_[index].pc_offset; + } + PcDescriptors::Kind Kind(int index) const { + return list_[index].kind; + } + intptr_t NodeId(int index) const { + return list_[index].node_id; + } + intptr_t TokenIndex(int index) const { + return list_[index].token_index; + } + intptr_t TryIndex(int index) const { + return list_[index].try_index; + } + + void AddDescriptor(PcDescriptors::Kind kind, + intptr_t pc_offset, + intptr_t node_id, + intptr_t token_index, + intptr_t try_index) { + struct PcDesc data; + data.pc_offset = pc_offset; + data.kind = kind; + data.node_id = node_id; + data.token_index = token_index; + data.try_index = try_index; + list_.Add(data); + } + + RawPcDescriptors* FinalizePcDescriptors(uword entry_point) { + intptr_t num_descriptors = Length(); + const PcDescriptors& descriptors = + PcDescriptors::Handle(PcDescriptors::New(num_descriptors)); + for (intptr_t i = 0; i < num_descriptors; i++) { + descriptors.AddDescriptor(i, + (entry_point + PcOffset(i)), + Kind(i), + NodeId(i), + TokenIndex(i), + TryIndex(i)); + } + return descriptors.raw(); + } + + private: + GrowableArray list_; + DISALLOW_COPY_AND_ASSIGN(DescriptorList); +}; + + +class CodeGenerator::HandlerList : public ZoneAllocated { + public: + struct HandlerDesc { + intptr_t try_index; // Try block index handled by the handler. + intptr_t pc_offset; // Handler PC offset value. + }; + + HandlerList() : list_() { + } + ~HandlerList() { } + + intptr_t Length() const { + return list_.length(); + } + + intptr_t TryIndex(int index) const { + return list_[index].try_index; + } + intptr_t PcOffset(int index) const { + return list_[index].pc_offset; + } + void SetPcOffset(int index, intptr_t handler_pc) { + list_[index].pc_offset = handler_pc; + } + + void AddHandler(intptr_t try_index, intptr_t pc_offset) { + struct HandlerDesc data; + data.try_index = try_index; + data.pc_offset = pc_offset; + list_.Add(data); + } + + RawExceptionHandlers* FinalizeExceptionHandlers(uword entry_point) { + intptr_t num_handlers = Length(); + const ExceptionHandlers& handlers = + ExceptionHandlers::Handle(ExceptionHandlers::New(num_handlers)); + for (intptr_t i = 0; i < num_handlers; i++) { + handlers.SetHandlerEntry(i, TryIndex(i), (entry_point + PcOffset(i))); + } + return handlers.raw(); + } + + private: + GrowableArray list_; + DISALLOW_COPY_AND_ASSIGN(HandlerList); +}; + + +CodeGenerator::CodeGenerator(Assembler* assembler, + const ParsedFunction& parsed_function) + : assembler_(assembler), + parsed_function_(parsed_function), + locals_space_size_(-1), + state_(NULL), + pc_descriptors_list_(NULL), + exception_handlers_list_(NULL), + try_index_(CatchClauseNode::kInvalidTryIndex) { + ASSERT(assembler_ != NULL); + ASSERT(parsed_function.node_sequence() != NULL); + pc_descriptors_list_ = new CodeGenerator::DescriptorList(); + exception_handlers_list_ = new CodeGenerator::HandlerList(); +} + + +bool CodeGenerator::IsResultNeeded(AstNode* node) const { + return !state()->IsRootNode(node); +} + + +// NOTE: First 5 bytes of the code may be patched with a jump instruction. Do +// not emit any objects in the first 5 bytes. +void CodeGenerator::GenerateCode() { + CodeGeneratorState codegen_state(this); + if (FLAG_print_scopes && FLAG_print_ast) { + // Print the function scope before code generation. + AstPrinter::PrintFunctionScope(parsed_function_); + } + if (FLAG_print_ast) { + // Print the function ast before code generation. + AstPrinter::PrintFunctionNodes(parsed_function_); + } + if (FLAG_trace_functions) { + // Preserve ECX (function name or object) and EDX (arguments descriptor). + __ pushl(ECX); + __ pushl(EDX); + const Function& function = + Function::ZoneHandle(parsed_function_.function().raw()); + __ LoadObject(EAX, function); + __ pushl(EAX); + GenerateCallRuntime(0, kTraceFunctionEntryRuntimeEntry); + __ popl(EAX); + __ popl(EDX); + __ popl(ECX); + } + + const bool code_generation_finished = TryIntrinsify(); + // In some cases intrinsifier can generate all code and no AST based + // code generation is needed. In some cases slow-paths (e.g., overflows) are + // implemented by the AST based code generation and 'code_generation_finished' + // is false. + if (!code_generation_finished) { + GeneratePreEntryCode(); + GenerateEntryCode(); + if (FLAG_print_scopes) { + // Print the function scope (again) after generating the prologue in order + // to see annotations such as allocation indices of locals. + if (FLAG_print_ast) { + // Second printing. + OS::Print("Annotated "); + } + AstPrinter::PrintFunctionScope(parsed_function_); + } + parsed_function_.node_sequence()->Visit(this); + } + // End of code. + __ int3(); + GenerateDeferredCode(); + + // Emit function patching code. This will be swapped with the first 5 bytes + // at entry point. + pc_descriptors_list_->AddDescriptor(PcDescriptors::kPatchCode, + assembler_->CodeSize(), + AstNode::kInvalidId, + 0, + -1); + __ jmp(&StubCode::FixCallersTargetLabel()); +} + + +void CodeGenerator::GenerateDeferredCode() { +} + + +// Pre entry code is called before the frame has been constructed: +// - check for stack overflow. +// - optionally count function invocations. +// - optionally trigger optimizing compiler if invocation threshold has been +// reached. +// Note that first 5 bytes may be patched with a jump. +// TODO(srdjan): Add check that no object is inlined in the first +// 5 bytes (length of a jump instruction). +void CodeGenerator::GeneratePreEntryCode() { + // Stack overflow check. + __ cmpl(ESP, + Address::Absolute(Isolate::Current()->stack_limit_address())); + __ j(BELOW_EQUAL, &StubCode::StackOverflowLabel()); + // Do not optimize if: + // - we count invocations. + // - optimization disabled via negative 'optimization_invocation_threshold; + // - function is marked as non-optimizable. + // - type checks are enabled. + const bool may_optimize = + !FLAG_report_invocation_count && + (FLAG_optimization_invocation_threshold >= 0) && + parsed_function_.function().is_optimizable() && + !FLAG_enable_type_checks; + // Count invocation and check. + if (FLAG_report_invocation_count || may_optimize) { + const Function& function = + Function::ZoneHandle(parsed_function_.function().raw()); + __ LoadObject(EAX, function); + __ movl(EBX, FieldAddress(EAX, Function::invocation_counter_offset())); + __ incl(EBX); + if (may_optimize) { + __ cmpl(EBX, Immediate(FLAG_optimization_invocation_threshold)); + __ j(GREATER, &StubCode::OptimizeInvokedFunctionLabel()); + } + __ movl(FieldAddress(EAX, Function::invocation_counter_offset()), EBX); + } +} + + +// Verify assumptions (in debug mode only). +// - No two deopt descriptors have the same node id (deoptimization). +// - No two ic-call descriptors have the same node id (type feedback). +// - No two descriptors of same kind have the same PC. +// A function without unique ids is marked as non-optimizable (e.g., because of +// finally blocks). +static bool VerifyPcDescriptors(const PcDescriptors& descriptors, + bool check_ids) { +#if defined(DEBUG) + // TODO(srdjan): Implement a more efficient way to check, currently drop + // the check for too large number of descriptors. + if (descriptors.Length() > 1000) { + if (FLAG_trace_compiler) { + OS::Print("Not checking pc decriptors, length %d\n", + descriptors.Length()); + } + return false; + } + for (intptr_t i = 0; i < descriptors.Length(); i++) { + intptr_t pc = descriptors.PC(i); + PcDescriptors::Kind kind = descriptors.DescriptorKind(i); + // 'node_id' is set for kDeopt and kIcCall and must be unique for one kind. + intptr_t node_id = AstNode::kInvalidId; + if (check_ids) { + if ((descriptors.DescriptorKind(i) == PcDescriptors::kDeopt) || + (descriptors.DescriptorKind(i) == PcDescriptors::kIcCall)) { + node_id = descriptors.NodeId(i); + } + } + for (intptr_t k = i + 1; k < descriptors.Length(); k++) { + if (kind == descriptors.DescriptorKind(k)) { + if (node_id != AstNode::kInvalidId) { + ASSERT(descriptors.NodeId(k) != node_id); + } + ASSERT(pc != descriptors.PC(k)); + } + } + } +#endif // DEBUG + return true; +} + + +void CodeGenerator::FinalizePcDescriptors(const Code& code) { + ASSERT(pc_descriptors_list_ != NULL); + const PcDescriptors& descriptors = PcDescriptors::Handle( + pc_descriptors_list_->FinalizePcDescriptors(code.EntryPoint())); + bool ok = VerifyPcDescriptors( + descriptors, parsed_function_.function().is_optimizable()); + if (!ok) { + parsed_function_.function().set_is_optimizable(false); + } + code.set_pc_descriptors(descriptors); +} + + +void CodeGenerator::FinalizeExceptionHandlers(const Code& code) { + ASSERT(exception_handlers_list_ != NULL); + const ExceptionHandlers& handlers = ExceptionHandlers::Handle( + exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint())); + code.set_exception_handlers(handlers); +} + + +void CodeGenerator::GenerateLoadVariable(Register dst, + const LocalVariable& variable) { + if (variable.is_captured()) { + // The variable lives in the context. + int delta = state()->context_level() - variable.owner()->context_level(); + ASSERT(delta >= 0); + Register base = CTX; + while (delta-- > 0) { + __ movl(dst, FieldAddress(base, Context::parent_offset())); + base = dst; + } + __ movl(dst, + FieldAddress(base, Context::variable_offset(variable.index()))); + } else { + // The variable lives in the current stack frame. + __ movl(dst, Address(EBP, variable.index() * kWordSize)); + } +} + + +void CodeGenerator::GenerateStoreVariable(const LocalVariable& variable, + Register src, + Register scratch) { + if (variable.is_captured()) { + // The variable lives in the context. + int delta = state()->context_level() - variable.owner()->context_level(); + ASSERT(delta >= 0); + Register base = CTX; + while (delta-- > 0) { + __ movl(scratch, FieldAddress(base, Context::parent_offset())); + base = scratch; + } + __ movl(FieldAddress(base, Context::variable_offset(variable.index())), + src); + } else { + // The variable lives in the current stack frame. + __ movl(Address(EBP, variable.index() * kWordSize), src); + } +} + + +void CodeGenerator::GeneratePushVariable(const LocalVariable& variable, + Register scratch) { + if (variable.is_captured()) { + // The variable lives in the context. + int delta = state()->context_level() - variable.owner()->context_level(); + ASSERT(delta >= 0); + Register base = CTX; + while (delta-- > 0) { + __ movl(scratch, FieldAddress(base, Context::parent_offset())); + base = scratch; + } + __ pushl(FieldAddress(base, Context::variable_offset(variable.index()))); + } else { + // The variable lives in the current stack frame. + __ pushl(Address(EBP, variable.index() * kWordSize)); + } +} + + +void CodeGenerator::GenerateInstanceCall(intptr_t node_id, + intptr_t token_index, + const String& function_name, + int arg_count, + ArgumentListNode* arguments) { + // Set up the function name and number of arguments (including the receiver) + // to the InstanceCall stub which will resolve the correct entrypoint for + // the operator and call it. + __ LoadObject(ECX, function_name); + __ LoadObject(EDX, ArgumentsDescriptor(arg_count, arguments)); + __ call(&StubCode::CallInstanceFunctionLabel()); + AddCurrentDescriptor(PcDescriptors::kIcCall, + node_id, + token_index); + __ addl(ESP, Immediate(arg_count * kWordSize)); +} + + +// Call to generate entry code: +// - compute frame size and setup frame. +// - allocate local variables on stack. +// - optionally check if number of arguments match. +// - initialize all non-argument locals to null. +// +// Input parameters: +// ESP : points to return address. +// ESP + 4 : address of last argument (arg n-1). +// ESP + 4*n : address of first argument (arg 0). +// EDX : arguments descriptor array. +void CodeGenerator::GenerateEntryCode() { + const Immediate raw_null = + Immediate(reinterpret_cast(Object::null())); + const Function& function = parsed_function_.function(); + LocalScope* scope = parsed_function_.node_sequence()->scope(); + const int num_fixed_params = function.num_fixed_parameters(); + const int num_opt_params = function.num_optional_parameters(); + const int num_params = num_fixed_params + num_opt_params; + int first_param_index; + int first_local_index; + int num_copied_params; + // Assign indices to parameters and locals. + if (num_params == num_fixed_params) { + // No need to copy incoming arguments. + // The body of the function will access parameter i at fp[1 + num_fixed - i] + // and local variable j at fp[-1 - j]. + first_param_index = 1 + num_params; + first_local_index = -1; + num_copied_params = 0; + } else { + // The body of the function will access copied parameter i at fp[-1 - i] + // and local j at fp[-1 - num_params - j]. + first_param_index = -1; + first_local_index = -1 - num_params; + num_copied_params = num_params; + ASSERT(num_copied_params > 0); + } + + // Allocate parameters and local variables, either in the local frame or in + // the context(s). + LocalScope* context_owner = NULL; // No context needed so far. + int first_free_frame_index = + scope->AllocateVariables(first_param_index, + num_params, + first_local_index, + scope, // Initial loop owner. + &context_owner); + // Frame indices are relative to the frame pointer and are decreasing. + ASSERT(first_free_frame_index <= first_local_index); + const int num_locals = first_local_index - first_free_frame_index; + + // Reserve local space for copied incoming and default arguments and locals. + // TODO(regis): We may give up reserving space on stack for args/locals + // because pushes of initial values may be more effective than moves. + set_locals_space_size((num_copied_params + num_locals) * kWordSize); + __ EnterFrame(locals_space_size()); + + // We check the number of passed arguments when we have to copy them due to + // the presence of optional named parameters. + // No such checking code is generated if only fixed parameters are declared, + // unless we are debug mode or unless we are compiling a closure. + if (num_copied_params == 0) { +#if defined(DEBUG) + const bool check_arguments = true; // Always check arguments in debug mode. +#else + // The number of arguments passed to closure functions must always be + // checked here, because no resolving stub (normally responsible for the + // check) is involved in closure calls. + const bool check_arguments = function.IsClosureFunction(); +#endif + if (check_arguments) { + // Check that num_fixed <= argc <= num_params. + Label argc_in_range; + // Total number of args is the first Smi in args descriptor array (EDX). + __ movl(EAX, FieldAddress(EDX, Array::data_offset())); + if (num_opt_params == 0) { + __ cmpl(EAX, Immediate(Smi::RawValue(num_fixed_params))); + __ j(EQUAL, &argc_in_range, Assembler::kNearJump); + } else { + __ subl(EAX, Immediate(Smi::RawValue(num_fixed_params))); + __ cmpl(EAX, Immediate(Smi::RawValue(num_opt_params))); + __ j(BELOW_EQUAL, &argc_in_range, Assembler::kNearJump); + } + if (function.IsClosureFunction()) { + GenerateCallRuntime(function.token_index(), + kClosureArgumentMismatchRuntimeEntry); + } else { + __ Stop("Wrong number of arguments"); + } + __ Bind(&argc_in_range); + } + } else { + ASSERT(first_param_index == -1); + // Copy positional arguments. + // Check that no fewer than num_fixed_params positional arguments are passed + // in and that no more than num_params arguments are passed in. + // Passed argument i at fp[1 + argc - i] copied to fp[-1 - i]. + + // Total number of args is the first Smi in args descriptor array (EDX). + __ movl(EBX, FieldAddress(EDX, Array::data_offset())); + // Check that num_args <= num_params. + Label wrong_num_arguments; + __ cmpl(EBX, Immediate(Smi::RawValue(num_params))); + __ j(GREATER, &wrong_num_arguments); + // Number of positional args is the second Smi in descriptor array (EDX). + __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize))); + // Check that num_pos_args >= num_fixed_params. + __ cmpl(ECX, Immediate(Smi::RawValue(num_fixed_params))); + __ j(LESS, &wrong_num_arguments); + // Since EBX and ECX are Smi, use TIMES_2 instead of TIMES_4. + // Let EBX point to the last passed positional argument, i.e. to + // fp[1 + num_args - (num_pos_args - 1)]. + __ subl(EBX, ECX); + __ leal(EBX, Address(EBP, EBX, TIMES_2, 2 * kWordSize)); + // Let EDI point to the last copied positional argument, i.e. to + // fp[-1 - (num_pos_args - 1)]. + __ movl(EDI, EBP); + __ subl(EDI, ECX); // ECX is a Smi, subtract twice for TIMES_4 scaling. + __ subl(EDI, ECX); + __ SmiUntag(ECX); + Label loop, loop_condition; + __ jmp(&loop_condition, Assembler::kNearJump); + // We do not use the final allocation index of the variable here, i.e. + // scope->VariableAt(i)->index(), because captured variables still need + // to be copied to the context that is not yet allocated. + const Address argument_addr(EBX, ECX, TIMES_4, 0); + const Address copy_addr(EDI, ECX, TIMES_4, 0); + __ Bind(&loop); + __ movl(EAX, argument_addr); + __ movl(copy_addr, EAX); + __ Bind(&loop_condition); + __ decl(ECX); + __ j(POSITIVE, &loop, Assembler::kNearJump); + + // Copy or initialize optional named arguments. + ASSERT(num_opt_params > 0); // Or we would not have to copy arguments. + // Start by alphabetically sorting the names of the optional parameters. + LocalVariable** opt_param = new LocalVariable*[num_opt_params]; + int* opt_param_position = new int[num_opt_params]; + for (int pos = num_fixed_params; pos < num_params; pos++) { + LocalVariable* parameter = scope->VariableAt(pos); + const String& opt_param_name = parameter->name(); + int i = pos - num_fixed_params; + while (--i >= 0) { + LocalVariable* param_i = opt_param[i]; + const intptr_t result = opt_param_name.CompareTo(param_i->name()); + ASSERT(result != 0); + if (result > 0) break; + opt_param[i + 1] = opt_param[i]; + opt_param_position[i + 1] = opt_param_position[i]; + } + opt_param[i + 1] = parameter; + opt_param_position[i + 1] = pos; + } + // Generate code handling each optional parameter in alphabetical order. + // Total number of args is the first Smi in args descriptor array (EDX). + __ movl(EBX, FieldAddress(EDX, Array::data_offset())); + // Number of positional args is the second Smi in descriptor array (EDX). + __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize))); + __ SmiUntag(ECX); + // Let EBX point to the first passed argument, i.e. to fp[1 + argc - 0]. + __ leal(EBX, Address(EBP, EBX, TIMES_2, kWordSize)); + // Let EDI point to the name/pos pair of the first named argument. + __ leal(EDI, FieldAddress(EDX, Array::data_offset() + (2 * kWordSize))); + for (int i = 0; i < num_opt_params; i++) { + // Handle this optional parameter only if k or fewer positional arguments + // have been passed, where k is the position of this optional parameter in + // the formal parameter list. + Label load_default_value, assign_optional_parameter, next_parameter; + const int param_pos = opt_param_position[i]; + __ cmpl(ECX, Immediate(param_pos)); + __ j(GREATER, &next_parameter, Assembler::kNearJump); + // Check if this named parameter was passed in. + __ movl(EAX, Address(EDI, 0)); // Load EAX with the name of the argument. + __ CompareObject(EAX, opt_param[i]->name()); + __ j(NOT_EQUAL, &load_default_value, Assembler::kNearJump); + // Load EAX with passed-in argument at provided arg_pos, i.e. at + // fp[1 + argc - arg_pos]. + __ movl(EAX, Address(EDI, kWordSize)); // EAX is arg_pos as Smi. + __ addl(EDI, Immediate(2 * kWordSize)); // Point to next name/pos pair. + __ negl(EAX); + Address argument_addr(EBX, EAX, TIMES_2, 0); // EAX is a negative Smi. + __ movl(EAX, argument_addr); + __ jmp(&assign_optional_parameter, Assembler::kNearJump); + __ Bind(&load_default_value); + // Load EAX with default argument at pos. + const Object& value = Object::ZoneHandle( + parsed_function_.default_parameter_values().At( + param_pos - num_fixed_params)); + __ LoadObject(EAX, value); + __ Bind(&assign_optional_parameter); + // Assign EAX to fp[-1 - param_pos]. + // We do not use the final allocation index of the variable here, i.e. + // scope->VariableAt(i)->index(), because captured variables still need + // to be copied to the context that is not yet allocated. + const Address param_addr(EBP, (-1 - param_pos) * kWordSize); + __ movl(param_addr, EAX); + __ Bind(&next_parameter); + } + delete[] opt_param; + delete[] opt_param_position; + // Check that EDI now points to the null terminator in the array descriptor. + Label all_arguments_processed; + __ cmpl(Address(EDI, 0), raw_null); + __ j(EQUAL, &all_arguments_processed, Assembler::kNearJump); + + __ Bind(&wrong_num_arguments); + if (function.IsClosureFunction()) { + GenerateCallRuntime(function.token_index(), + kClosureArgumentMismatchRuntimeEntry); + } else { + // Invoke noSuchMethod function. + __ LoadObject(ECX, String::ZoneHandle(function.name())); + // EBP : points to previous frame pointer. + // EBP + 4 : points to return address. + // EBP + 8 : address of last argument (arg n-1). + // ESP + 8 + 4*(n-1) : address of first argument (arg 0). + // ECX : function name. + // EDX : arguments descriptor array. + __ call(&StubCode::CallNoSuchMethodFunctionLabel()); + } + + if (FLAG_trace_functions) { + __ pushl(EAX); // Preserve result. + __ PushObject(function); + GenerateCallRuntime(0, kTraceFunctionExitRuntimeEntry); + __ popl(EAX); // Remove argument. + __ popl(EAX); // Restore result. + } + __ LeaveFrame(); + __ ret(); + + __ Bind(&all_arguments_processed); + // Nullify originally passed arguments only after they have been copied and + // checked, otherwise noSuchMethod would not see their original values. + // This step can be skipped in case we decide that formal parameters are + // implicitly final, since garbage collecting the unmodified value is not + // an issue anymore. + + // EDX : arguments descriptor array. + // Total number of args is the first Smi in args descriptor array (EDX). + __ movl(ECX, FieldAddress(EDX, Array::data_offset())); + __ SmiUntag(ECX); + Label null_args_loop, null_args_loop_condition; + __ jmp(&null_args_loop_condition, Assembler::kNearJump); + const Address original_argument_addr(EBP, ECX, TIMES_4, 2 * kWordSize); + __ Bind(&null_args_loop); + __ movl(original_argument_addr, raw_null); + __ Bind(&null_args_loop_condition); + __ decl(ECX); + __ j(POSITIVE, &null_args_loop, Assembler::kNearJump); + } + + // Initialize locals. + // TODO(regis): For now, always unroll the init loop. Decide later above + // which threshold to implement a loop. + // Consider emitting pushes instead of moves. + for (int index = first_local_index; index > first_free_frame_index; index--) { + if (index == first_local_index) { + __ movl(EAX, raw_null); + } + __ movl(Address(EBP, index * kWordSize), EAX); + } +} + + +void CodeGenerator::VisitReturnNode(ReturnNode* node) { + ASSERT(!IsResultNeeded(node)); + ASSERT(node->value() != NULL); + + if (!node->value()->IsLiteralNode()) { + node->value()->Visit(this); + // The result of the return value is now on top of the stack. + } + + // Generate inlined code for all finally blocks as we are about to transfer + // control out of the 'try' blocks if any. + for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) { + node->InlinedFinallyNodeAt(i)->Visit(this); + } + + if (node->value()->IsLiteralNode()) { + // Load literal value into EAX. + const Object& literal = node->value()->AsLiteralNode()->literal(); + if (literal.IsSmi()) { + __ movl(EAX, Immediate(reinterpret_cast(literal.raw()))); + } else { + __ LoadObject(EAX, literal); + } + } else { + // Pop the previously evaluated result value into EAX. + __ popl(EAX); + } + + // Generate type check. + if (FLAG_enable_type_checks) { + GenerateAssertAssignable( + node->value()->token_index(), + Type::ZoneHandle(parsed_function().function().result_type()), + String::ZoneHandle(String::NewSymbol("function result"))); + } + // Unchain the context(s) up to context level 0. + int context_level = state()->context_level(); + ASSERT(context_level >= 0); + while (context_level-- > 0) { + __ movl(CTX, FieldAddress(CTX, Context::parent_offset())); + } +#ifdef DEBUG + // Check that the entry stack size matches the exit stack size. + __ movl(EDX, EBP); + __ subl(EDX, ESP); + ASSERT(locals_space_size() >= 0); + __ cmpl(EDX, Immediate(locals_space_size())); + Label wrong_stack; + __ j(NOT_EQUAL, &wrong_stack, Assembler::kNearJump); +#endif // DEBUG. + + if (FLAG_trace_functions) { + __ pushl(EAX); // Preserve result. + const Function& function = + Function::ZoneHandle(parsed_function_.function().raw()); + __ LoadObject(EBX, function); + __ pushl(EBX); + GenerateCallRuntime(0, kTraceFunctionExitRuntimeEntry); + __ popl(EAX); // Remove argument. + __ popl(EAX); // Restore result. + } + __ LeaveFrame(); + __ ret(); + +#ifdef DEBUG + __ Bind(&wrong_stack); + __ Stop("Exit stack size does not match the entry stack size."); +#endif // DEBUG. +} + + +void CodeGenerator::VisitLiteralNode(LiteralNode* node) { + if (!IsResultNeeded(node)) return; + const Object& literal = node->literal(); + if (literal.IsSmi()) { + __ pushl(Immediate(reinterpret_cast(literal.raw()))); + } else { + __ PushObject(literal); + } +} + + +void CodeGenerator::VisitTypeNode(TypeNode* node) { + // Type nodes are handled specially by the code generator. + UNREACHABLE(); +} + + +void CodeGenerator::VisitClosureNode(ClosureNode* node) { + const int current_context_level = state()->context_level(); + const ContextScope& context_scope = ContextScope::ZoneHandle( + node->scope()->PreserveOuterScope(current_context_level)); + const Function& function = node->function(); + ASSERT(!function.HasCode()); + ASSERT(function.context_scope() == ContextScope::null()); + function.set_context_scope(context_scope); + const Code& stub = Code::Handle( + StubCode::GetAllocationStubForClosure(function)); + const ExternalLabel label(function.ToCString(), stub.EntryPoint()); + GenerateCall(node->token_index(), &label); + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitStaticImplicitClosureNode( + StaticImplicitClosureNode* node) { + const Function& function = node->function(); + ASSERT(function.context_scope() != ContextScope::null()); + const Code& stub = Code::Handle( + StubCode::GetAllocationStubForStaticImplicitClosure(function)); + const ExternalLabel label(function.ToCString(), stub.EntryPoint()); + GenerateCall(node->token_index(), &label); + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitImplicitClosureNode(ImplicitClosureNode* node) { + const Function& function = node->function(); + ASSERT(function.context_scope() != ContextScope::null()); + const Immediate raw_null = + Immediate(reinterpret_cast(Object::null())); + Label null_receiver; + node->receiver()->Visit(this); + __ popl(EAX); // Get receiver. + const Object& result = Object::ZoneHandle(); + __ PushObject(result); // Make room for the result of the runtime call. + __ PushObject(function); // Push the type. + __ pushl(EAX); // Push the receiver. + GenerateCallRuntime(node->token_index(), + kAllocateImplicitClosureRuntimeEntry); + // Pop the parameters supplied to the runtime entry. The result of the + // type check runtime call is the checked value. + __ addl(ESP, Immediate(2 * kWordSize)); + __ popl(EAX); // Allocated closure. + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitPrimaryNode(PrimaryNode* node) { + // PrimaryNodes are temporary during parsing. + ErrorMsg(node->token_index(), + "Unexpected primary node: %s", node->primary().ToCString()); +} + + +void CodeGenerator::VisitSequenceNode(SequenceNode* node_sequence) { + CodeGeneratorState codegen_state(this); + LocalScope* scope = node_sequence->scope(); + ASSERT(scope != NULL); + intptr_t num_context_variables = scope->num_context_variables(); + if (num_context_variables > 0) { + // The loop local scope declares variables that are captured. + // Allocate and chain a new context. + __ movl(EDX, Immediate(num_context_variables)); + const ExternalLabel label("alloc_context", + StubCode::AllocateContextEntryPoint()); + GenerateCall(node_sequence->token_index(), &label); + + // Chain the new context in EAX to its parent in CTX. + __ movl(FieldAddress(EAX, Context::parent_offset()), CTX); + // Set new context as current context. + __ movl(CTX, EAX); + state()->set_context_level(scope->context_level()); + + // If this node_sequence is the body of the function being compiled, copy + // the captured parameters from the frame into the context. + if (node_sequence == parsed_function_.node_sequence()) { + ASSERT(scope->context_level() == 1); + const Immediate raw_null = + Immediate(reinterpret_cast(Object::null())); + const Function& function = parsed_function_.function(); + const int num_params = function.NumberOfParameters(); + int param_frame_index = + (num_params == function.num_fixed_parameters()) ? 1 + num_params : -1; + for (int pos = 0; pos < num_params; param_frame_index--, pos++) { + LocalVariable* parameter = scope->VariableAt(pos); + ASSERT(parameter->owner() == scope); + if (parameter->is_captured()) { + // Copy parameter from local frame to current context. + const Address local_addr(EBP, param_frame_index * kWordSize); + __ movl(EAX, local_addr); + GenerateStoreVariable(*parameter, EAX, EDX); + // Write NULL to the source location to detect buggy accesses and + // allow GC of passed value if it gets overwritten by a new value in + // the function. + __ movl(local_addr, raw_null); + } + } + } + } + // If this node_sequence is the body of the function being compiled, generate + // code checking the type of the actual arguments. + if (FLAG_enable_type_checks && + (node_sequence == parsed_function_.node_sequence())) { + GenerateArgumentTypeChecks(); + } + for (int i = 0; i < node_sequence->length(); i++) { + AstNode* child_node = node_sequence->NodeAt(i); + state()->set_root_node(child_node); + child_node->Visit(this); + } + if (node_sequence->label() != NULL) { + __ Bind(node_sequence->label()->break_label()); + } + if (num_context_variables > 0) { + // Unchain the previously allocated context. + __ movl(CTX, FieldAddress(CTX, Context::parent_offset())); + } +} + + +void CodeGenerator::VisitArgumentListNode(ArgumentListNode* arguments) { + for (int i = 0; i < arguments->length(); i++) { + AstNode* argument = arguments->NodeAt(i); + argument->Visit(this); + } +} + + +void CodeGenerator::VisitArrayNode(ArrayNode* node) { + // Evaluate the array elements. + for (int i = 0; i < node->length(); i++) { + AstNode* element = node->ElementAt(i); + element->Visit(this); + } + + // Allocate the array. + // EDX : Array length as Smi. + // ECX : element type for the array. + __ movl(EDX, Immediate(Smi::RawValue(node->length()))); + const TypeArguments& element_type = node->type_arguments(); + ASSERT(element_type.IsNull() || element_type.IsInstantiated()); + __ LoadObject(ECX, element_type); + GenerateCall(node->token_index(), &StubCode::AllocateArrayLabel()); + + // Pop the element values from the stack into the array. + __ leal(ECX, FieldAddress(EAX, Array::data_offset())); + for (int i = node->length() - 1; i >= 0; i--) { + __ popl(Address(ECX, i * kWordSize)); + } + + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitLoadLocalNode(LoadLocalNode* node) { + // Load the value of the local variable and push it onto the expression stack. + if (IsResultNeeded(node)) { + GeneratePushVariable(node->local(), EAX); + } +} + + +void CodeGenerator::VisitStoreLocalNode(StoreLocalNode* node) { + node->value()->Visit(this); + __ popl(EAX); + if (FLAG_enable_type_checks) { + GenerateAssertAssignable(node->value()->token_index(), + node->local().type(), + node->local().name()); + } + GenerateStoreVariable(node->local(), EAX, EDX); + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitLoadInstanceFieldNode(LoadInstanceFieldNode* node) { + node->instance()->Visit(this); + MarkDeoptPoint(node->id(), node->token_index()); + __ popl(EAX); // Instance. + __ movl(EAX, FieldAddress(EAX, node->field().Offset())); + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitStoreInstanceFieldNode(StoreInstanceFieldNode* node) { + node->instance()->Visit(this); + node->value()->Visit(this); + MarkDeoptPoint(node->id(), node->token_index()); + __ popl(EAX); // Value. + if (FLAG_enable_type_checks) { + GenerateAssertAssignable(node->value()->token_index(), + Type::ZoneHandle(node->field().type()), + String::ZoneHandle(node->field().name())); + } + __ popl(EDX); // Instance. + __ StoreIntoObject(EDX, FieldAddress(EDX, node->field().Offset()), EAX); + if (IsResultNeeded(node)) { + // The result is the input value. + __ pushl(EAX); + } +} + + +// Expects array and index on stack and returns result in EAX. +void CodeGenerator::GenerateLoadIndexed(intptr_t node_id, + intptr_t token_index) { + // Invoke the [] operator on the receiver object with the index as argument. + const String& operator_name = + String::ZoneHandle(String::NewSymbol(Token::Str(Token::kINDEX))); + const int kNumArguments = 2; // Receiver and index. + GenerateInstanceCall(node_id, + token_index, + operator_name, + kNumArguments, + NULL); +} + + +void CodeGenerator::VisitLoadIndexedNode(LoadIndexedNode* node) { + node->array()->Visit(this); + // Now compute the index. + node->index_expr()->Visit(this); + MarkDeoptPoint(node->id(), node->token_index()); + GenerateLoadIndexed(node->id(), node->token_index()); + // Result is in EAX. + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +// Expected arguments. +// TOS(0): value. +// TOS(1): index. +// TOS(2): array. +void CodeGenerator::GenerateStoreIndexed(intptr_t node_id, + intptr_t token_index, + bool preserve_value) { + // It is not necessary to generate a type test of the assigned value here, + // because the []= operator will check the type of its incoming arguments. + if (preserve_value) { + __ popl(EAX); + __ popl(EDX); + __ popl(ECX); + __ pushl(EAX); // Preserve stored value. + __ pushl(ECX); // Restore arguments. + __ pushl(EDX); + __ pushl(EAX); + } + // Invoke the []= operator on the receiver object with index and + // value as arguments. + const String& operator_name = + String::ZoneHandle(String::NewSymbol(Token::Str(Token::kASSIGN_INDEX))); + const int kNumArguments = 3; // Receiver, index and value. + GenerateInstanceCall(node_id, + token_index, + operator_name, + kNumArguments, + NULL); +} + + +void CodeGenerator::VisitStoreIndexedNode(StoreIndexedNode* node) { + // Compute the receiver object and pass as first argument to call. + node->array()->Visit(this); + // Now compute the index. + node->index_expr()->Visit(this); + // Finally compute the value to assign. + node->value()->Visit(this); + MarkDeoptPoint(node->id(), node->token_index()); + GenerateStoreIndexed(node->id(), node->token_index(), IsResultNeeded(node)); +} + + +void CodeGenerator::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) { + MarkDeoptPoint(node->id(), node->token_index()); + __ LoadObject(EDX, node->field()); + __ movl(EAX, FieldAddress(EDX, Field::value_offset())); + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitStoreStaticFieldNode(StoreStaticFieldNode* node) { + node->value()->Visit(this); + MarkDeoptPoint(node->id(), node->token_index()); + __ popl(EAX); // Value. + if (FLAG_enable_type_checks) { + GenerateAssertAssignable(node->value()->token_index(), + Type::ZoneHandle(node->field().type()), + String::ZoneHandle(node->field().name())); + } + __ LoadObject(EDX, node->field()); + __ StoreIntoObject(EDX, FieldAddress(EDX, Field::value_offset()), EAX); + if (IsResultNeeded(node)) { + // The result is the input value. + __ pushl(EAX); + } +} + + +void CodeGenerator::GenerateLogicalNotOp(UnaryOpNode* node) { + // Generate false if operand is true, otherwise generate true. + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + const Bool& bool_false = Bool::ZoneHandle(Bool::False()); + node->operand()->Visit(this); + MarkDeoptPoint(node->id(), node->token_index()); + Label done; + GenerateConditionTypeCheck(node->operand()->token_index()); + __ popl(EDX); + __ LoadObject(EAX, bool_true); + __ cmpl(EAX, EDX); + __ j(NOT_EQUAL, &done, Assembler::kNearJump); + __ LoadObject(EAX, bool_false); + __ Bind(&done); + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitUnaryOpNode(UnaryOpNode* node) { + if (node->kind() == Token::kNOT) { + // "!" cannot be overloaded, therefore inline it. + GenerateLogicalNotOp(node); + return; + } + node->operand()->Visit(this); + MarkDeoptPoint(node->id(), node->token_index()); + if (node->kind() == Token::kADD) { + // Unary operator '+' does not exist, it's a NOP, skip it. + if (!IsResultNeeded(node)) { + __ popl(EAX); + } + return; + } + String& operator_name = String::ZoneHandle(); + if (node->kind() == Token::kSUB) { + operator_name = String::NewSymbol(Token::Str(Token::kNEGATE)); + } else { + operator_name = String::NewSymbol(node->Name()); + } + const int kNumberOfArguments = 1; + GenerateInstanceCall(node->id(), + node->token_index(), + operator_name, + kNumberOfArguments, + NULL); + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitIncrOpLocalNode(IncrOpLocalNode* node) { + ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR)); + MarkDeoptPoint(node->id(), node->token_index()); + GenerateLoadVariable(EAX, node->local()); + if (!node->prefix() && IsResultNeeded(node)) { + // Preserve as result. + __ pushl(EAX); + } + const Immediate value = Immediate(reinterpret_cast(Smi::New(1))); + const char* operator_name = (node->kind() == Token::kINCR) ? "+" : "-"; + __ pushl(EAX); + __ pushl(value); + GenerateBinaryOperatorCall(node->id(), node->token_index(), operator_name); + // result is in EAX. + if (FLAG_enable_type_checks) { + GenerateAssertAssignable( + node->token_index(), node->local().type(), node->local().name()); + } + GenerateStoreVariable(node->local(), EAX, EDX); + if (node->prefix() && IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitIncrOpInstanceFieldNode( + IncrOpInstanceFieldNode* node) { + ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR)); + node->receiver()->Visit(this); + MarkDeoptPoint(node->id(), node->token_index()); + __ pushl(Address(ESP, 0)); // Duplicate receiver (preserve for setter). + GenerateInstanceGetterCall(node->getter_id(), + node->token_index(), + node->field_name()); + // result is in EAX. + __ popl(EDX); // Get receiver. + if (!node->prefix() && IsResultNeeded(node)) { + // Preserve as result. + __ pushl(EAX); // Preserve value as result. + } + const Immediate one_value = Immediate(reinterpret_cast(Smi::New(1))); + const char* operator_name = (node->kind() == Token::kINCR) ? "+" : "-"; + // EAX: Value. + // EDX: Receiver. + __ pushl(EDX); // Preserve receiver. + __ pushl(EAX); // Left operand. + __ pushl(one_value); // Right operand. + GenerateBinaryOperatorCall(node->operator_id(), + node->token_index(), + operator_name); + __ popl(EDX); // Restore receiver. + if (IsResultNeeded(node) && node->prefix()) { + // Value stored into field is the result. + __ pushl(EAX); + } + __ pushl(EDX); // Receiver. + __ pushl(EAX); // Value. + // It is not necessary to generate a type test of the assigned value here, + // because the setter will check the type of its incoming arguments. + GenerateInstanceSetterCall(node->setter_id(), + node->token_index(), + node->field_name()); +} + + +void CodeGenerator::VisitIncrOpStaticFieldNode(IncrOpStaticFieldNode* node) { + ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR)); + MarkDeoptPoint(node->id(), node->token_index()); + if (node->field().IsNull()) { + GenerateStaticGetterCall(node->token_index(), + node->field_class(), + node->field_name()); + } else { + __ LoadObject(EDX, node->field()); + __ movl(EAX, FieldAddress(EDX, Field::value_offset())); + } + // Value in EAX. + if (!node->prefix() && IsResultNeeded(node)) { + // Preserve as result. + __ pushl(EAX); + } + const Immediate value = Immediate(reinterpret_cast(Smi::New(1))); + const char* operator_name = (node->kind() == Token::kINCR) ? "+" : "-"; + __ pushl(EAX); // Left operand. + __ pushl(value); // Right operand. + GenerateBinaryOperatorCall(node->id(), node->token_index(), operator_name); + // result is in EAX. + if (node->prefix() && IsResultNeeded(node)) { + __ pushl(EAX); + } + if (node->field().IsNull()) { + __ pushl(EAX); + // It is not necessary to generate a type test of the assigned value here, + // because the setter will check the type of its incoming arguments. + GenerateStaticSetterCall(node->token_index(), + node->field_class(), + node->field_name()); + } else { + if (FLAG_enable_type_checks) { + GenerateAssertAssignable(node->token_index(), + Type::ZoneHandle(node->field().type()), + String::ZoneHandle(node->field().name())); + } + __ LoadObject(EDX, node->field()); + __ StoreIntoObject(EDX, FieldAddress(EDX, Field::value_offset()), EAX); + } +} + + +void CodeGenerator::VisitIncrOpIndexedNode(IncrOpIndexedNode* node) { + ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR)); + node->array()->Visit(this); + node->index()->Visit(this); + MarkDeoptPoint(node->id(), node->token_index()); + // Preserve array and index for GenerateStoreIndex. + __ pushl(Address(ESP, kWordSize)); // Copy array. + __ pushl(Address(ESP, kWordSize)); // Copy index. + GenerateLoadIndexed(node->load_id(), node->token_index()); + // Result is in EAX. + if (!node->prefix() && IsResultNeeded(node)) { + // Preserve EAX as result. + __ popl(EDX); // Preserved index -> EDX. + __ popl(ECX); // Preserved array -> ECX. + __ pushl(EAX); // Preserve original value from indexed load. + __ pushl(ECX); // Array. + __ pushl(EDX); // Index. + } + const Immediate value = Immediate(reinterpret_cast(Smi::New(1))); + const char* operator_name = (node->kind() == Token::kINCR) ? "+" : "-"; + __ pushl(EAX); // Left operand. + __ pushl(value); // Right operand. + GenerateBinaryOperatorCall(node->operator_id(), + node->token_index(), + operator_name); + __ pushl(EAX); + // TOS(0): value, TOS(1): index, TOS(2): array. + GenerateStoreIndexed(node->store_id(), + node->token_index(), + node->prefix() && IsResultNeeded(node)); +} + + +// Optimize instanceof type test by adding inlined tests for: +// - NULL -> return false. +// - Smi -> compile time subtype check (only if dst class is not parameterized). +// - Class equality (only if class is not parameterized). +// Inputs: +// - EAX: object. +// Destroys ECX. +// Returns: +// - true or false on stack. +void CodeGenerator::GenerateInstanceOf(intptr_t token_index, + const Type& type, + bool negate_result) { + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + const Bool& bool_false = Bool::ZoneHandle(Bool::False()); + + // All instances are of type Object. + const Type& object_type = + Type::Handle(Isolate::Current()->object_store()->object_type()); + if (type.IsInstantiated() && object_type.IsSubtypeOf(type)) { + // All objects are an instance of the Object class. + __ PushObject(negate_result ? bool_false : bool_true); + return; + } + + // A NULL object always returns false for all types other than Object + // (and Null). + const Immediate raw_null = + Immediate(reinterpret_cast(Object::null())); + Label non_null, done; + __ cmpl(EAX, raw_null); + __ j(NOT_EQUAL, &non_null, Assembler::kNearJump); + __ PushObject(negate_result ? bool_true : bool_false); + __ jmp(&done, Assembler::kNearJump); + + __ Bind(&non_null); + // If type is instantiated and non-parameterized, we can inline code + // checking whether the tested instance is a Smi. + if (type.IsInstantiated()) { + const Class& type_class = Class::ZoneHandle(type.type_class()); + const bool is_type_class_parameterized = type_class.IsParameterized(); + // A Smi object cannot be the instance of a parameterized class. + // A class equality check is only applicable to a non-parameterized class. + // TODO(regis): Should we still inline a Smi type check when checking for a + // parameterized type and return false for a Smi's without calling the + // runtime? + if (!is_type_class_parameterized) { + Label compare_classes; + __ testl(EAX, Immediate(kSmiTagMask)); + __ j(NOT_ZERO, &compare_classes, Assembler::kNearJump); + // Object is Smi. + const Class& smi_class = Class::Handle(Smi::Class()); + // TODO(regis): We should introduce a SmiType. + if (smi_class.IsSubtypeOf(TypeArguments::Handle(), + type_class, + TypeArguments::Handle())) { + __ PushObject(negate_result ? bool_false : bool_true); + } else { + __ PushObject(negate_result ? bool_true : bool_false); + } + __ jmp(&done, Assembler::kNearJump); + + // Compare if the classes are equal. + __ Bind(&compare_classes); + // If type is an interface, we can skip the class equality check, + // because instances cannot be of an interface type. + if (!type_class.is_interface()) { + Label runtime_call; + __ movl(ECX, FieldAddress(EAX, Object::class_offset())); + __ CompareObject(ECX, type_class); + __ j(NOT_EQUAL, &runtime_call, Assembler::kNearJump); + __ PushObject(negate_result ? bool_false : bool_true); + __ jmp(&done, Assembler::kNearJump); + __ Bind(&runtime_call); + } + } + } + const Object& result = Object::ZoneHandle(); + __ PushObject(result); // Make room for the result of the runtime call. + __ pushl(EAX); // Push the instance. + __ PushObject(type); // Push the type. + if (!type.IsInstantiated()) { + ASSERT(parsed_function().instantiator() != NULL); + parsed_function().instantiator()->Visit(this); // Instantiator on stack. + if (!parsed_function().function().IsInFactoryScope()) { + __ popl(EAX); // Pop instantiator. + const Class& instantiator_class = + Class::Handle(parsed_function().function().owner()); + // The instantiator is the receiver of the caller, which is not a factory. + // The receiver cannot be null; extract its TypeArguments object. + // Note that in the factory case, the instantiator is the first parameter + // of the factory, i.e. already a TypeArguments object. + intptr_t type_arguments_instance_field_offset = + instantiator_class.type_arguments_instance_field_offset(); + ASSERT(type_arguments_instance_field_offset != Class::kNoTypeArguments); + __ movl(EAX, FieldAddress(EAX, type_arguments_instance_field_offset)); + __ pushl(EAX); // Push instantiator. + } + } else { + __ PushObject(TypeArguments::ZoneHandle()); // Null instantiator. + } + GenerateCallRuntime(token_index, kInstanceofRuntimeEntry); + // Pop the two parameters supplied to the runtime entry. The result of the + // instanceof runtime call will be left as the result of the operation. + __ addl(ESP, Immediate(3 * kWordSize)); + if (negate_result) { + Label negate_done; + __ popl(EDX); + __ LoadObject(EAX, bool_true); + __ cmpl(EDX, EAX); + __ j(NOT_EQUAL, &negate_done, Assembler::kNearJump); + __ LoadObject(EAX, bool_false); + __ Bind(&negate_done); + __ pushl(EAX); + } + __ Bind(&done); +} + + +// Optimize assignable type check by adding inlined tests for: +// - NULL -> return NULL. +// - Smi -> compile time subtype check (only if dst class is not parameterized). +// - Class equality (only if class is not parameterized). +// Inputs: +// - EAX: object. +// Destroys ECX and EDX. +// Returns: +// - object in EAX for successful assignable check (or throws TypeError). +void CodeGenerator::GenerateAssertAssignable(intptr_t token_index, + const Type& dst_type, + const String& dst_name) { + ASSERT(FLAG_enable_type_checks); + ASSERT(token_index >= 0); + ASSERT(!dst_type.IsNull()); + ASSERT(dst_type.IsResolved()); + + // Any expression is assignable to the VarType. Skip the test. + if (dst_type.IsVarType()) { + return; + } + + // It is a compile-time error to explicitly return a value (including null) + // from a void function. However, functions that do not explicitly return a + // value, implicitly return null. This includes void functions. Therefore, we + // skip the type test here and trust the parser to only return null in void + // function. + if (dst_type.IsVoidType()) { + return; + } + + // A NULL object is always assignable and is returned as result. + const Immediate raw_null = + Immediate(reinterpret_cast(Object::null())); + Label done, runtime_call; + __ cmpl(EAX, raw_null); + __ j(EQUAL, &done, Assembler::kNearJump); + + // If dst_type is instantiated and non-parameterized, we can inline code + // checking whether the assigned instance is a Smi. + if (dst_type.IsInstantiated()) { + const Class& dst_type_class = Class::ZoneHandle(dst_type.type_class()); + const bool is_dst_type_parameterized = dst_type_class.IsParameterized(); + // A Smi object cannot be the instance of a parameterized class. + // A class equality check is only applicable to a non-parameterized class. + if (!is_dst_type_parameterized) { + Label compare_classes; + __ testl(EAX, Immediate(kSmiTagMask)); + __ j(NOT_ZERO, &compare_classes, Assembler::kNearJump); + // Object is Smi. + const Class& smi_class = Class::Handle(Smi::Class()); + // TODO(regis): We should introduce a SmiType. + if (smi_class.IsSubtypeOf(TypeArguments::Handle(), + dst_type_class, + TypeArguments::Handle())) { + // Successful assignable type check: return object in EAX. + __ jmp(&done, Assembler::kNearJump); + } else { + // Failed assignable type check: call runtime to throw TypeError. + __ jmp(&runtime_call, Assembler::kNearJump); + } + // Compare if the classes are equal. + __ Bind(&compare_classes); + // If dst_type is an interface, we can skip the class equality check, + // because instances cannot be of an interface type. + if (!dst_type_class.is_interface()) { + __ movl(ECX, FieldAddress(EAX, Object::class_offset())); + __ LoadObject(EDX, dst_type_class); + __ cmpl(EDX, ECX); + __ j(EQUAL, &done, Assembler::kNearJump); + } + } + } + __ Bind(&runtime_call); + const Object& result = Object::ZoneHandle(); + __ PushObject(result); // Make room for the result of the runtime call. + const Immediate location = + Immediate(reinterpret_cast(Smi::New(token_index))); + __ pushl(location); // Push the source location. + __ pushl(EAX); // Push the source object. + __ PushObject(dst_type); // Push the type of the destination. + if (!dst_type.IsInstantiated()) { + ASSERT(parsed_function().instantiator() != NULL); + parsed_function().instantiator()->Visit(this); // Instantiator on stack. + if (!parsed_function().function().IsInFactoryScope()) { + __ popl(EAX); // Pop instantiator. + const Class& instantiator_class = + Class::Handle(parsed_function().function().owner()); + // The instantiator is the receiver of the caller, which is not a factory. + // The receiver cannot be null; extract its TypeArguments object. + // Note that in the factory case, the instantiator is the first parameter + // of the factory, i.e. already a TypeArguments object. + intptr_t type_arguments_instance_field_offset = + instantiator_class.type_arguments_instance_field_offset(); + ASSERT(type_arguments_instance_field_offset != Class::kNoTypeArguments); + __ movl(EAX, FieldAddress(EAX, type_arguments_instance_field_offset)); + __ pushl(EAX); // Push instantiator. + } + } else { + __ PushObject(TypeArguments::ZoneHandle()); // Null instantiator. + } + __ PushObject(dst_name); // Push the name of the destination. + GenerateCallRuntime(token_index, kTypeCheckRuntimeEntry); + // Pop the parameters supplied to the runtime entry. The result of the + // type check runtime call is the checked value. + __ addl(ESP, Immediate(5 * kWordSize)); + __ popl(EAX); + + __ Bind(&done); +} + + +void CodeGenerator::GenerateArgumentTypeChecks() { + const Function& function = parsed_function_.function(); + LocalScope* scope = parsed_function_.node_sequence()->scope(); + const int num_fixed_params = function.num_fixed_parameters(); + const int num_opt_params = function.num_optional_parameters(); + ASSERT(num_fixed_params + num_opt_params <= scope->num_variables()); + for (int i = 0; i < num_fixed_params + num_opt_params; i++) { + LocalVariable* parameter = scope->VariableAt(i); + GenerateLoadVariable(EAX, *parameter); + GenerateAssertAssignable( + parameter->token_index(), parameter->type(), parameter->name()); + } +} + + +void CodeGenerator::GenerateConditionTypeCheck(intptr_t token_index) { + if (!FLAG_enable_type_checks) { + return; + } + + const Object& result = Object::ZoneHandle(); + __ PushObject(result); // Make room for the result of the runtime call. + const Immediate location = + Immediate(reinterpret_cast(Smi::New(token_index))); + __ pushl(location); // Push the source location. + __ pushl(Address(ESP, 2 * kWordSize)); // Push the source object. + GenerateCallRuntime(token_index, kConditionTypeCheckRuntimeEntry); + // Pop the parameters supplied to the runtime entry. The result of the + // type check runtime call is the checked value. + __ addl(ESP, Immediate(3 * kWordSize)); +} + + +void CodeGenerator::VisitComparisonNode(ComparisonNode* node) { + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + const Bool& bool_false = Bool::ZoneHandle(Bool::False()); + node->left()->Visit(this); + + // The instanceof operator needs special handling. + if (Token::IsInstanceofOperator(node->kind())) { + __ popl(EAX); // Left operand. + ASSERT(node->right()->IsTypeNode()); + GenerateInstanceOf(node->token_index(), + node->right()->AsTypeNode()->type(), + (node->kind() == Token::kISNOT)); + if (!IsResultNeeded(node)) { + __ popl(EAX); // Pop the result of the instanceof operation. + } + return; + } + + node->right()->Visit(this); + // Both left and right values on stack. + + // '===' and '!==' are not overloadable. + if ((node->kind() == Token::kEQ_STRICT) || + (node->kind() == Token::kNE_STRICT)) { + __ popl(EDX); // Right operand. + __ popl(EAX); // Left operand. + if (!IsResultNeeded(node)) { + return; + } + Label load_true, done; + __ cmpl(EAX, EDX); + if (node->kind() == Token::kEQ_STRICT) { + __ j(EQUAL, &load_true, Assembler::kNearJump); + } else { + __ j(NOT_EQUAL, &load_true, Assembler::kNearJump); + } + __ LoadObject(EAX, bool_false); + __ jmp(&done, Assembler::kNearJump); + __ Bind(&load_true); + __ LoadObject(EAX, bool_true); + __ Bind(&done); + // Result is in EAX. + __ pushl(EAX); + return; + } + + MarkDeoptPoint(node->id(), node->token_index()); + + // '!=' not overloadable, always implements negation of '=='. + // Call operator for '=='. + if ((node->kind() == Token::kEQ) || (node->kind() == Token::kNE)) { + // Null is a special receiver with a special type and frequently used on + // operators "==" and "!=". Emit inlined code for null so that it does not + // pollute type information at call site. + Label null_done; + { + const Immediate raw_null = + Immediate(reinterpret_cast(Object::null())); + Label non_null_compare, load_true; + // Check if left argument is null. + __ cmpl(Address(ESP, 1 * kWordSize), raw_null); + __ j(NOT_EQUAL, &non_null_compare, Assembler::kNearJump); + // Comparison with NULL is "===". + // Load/remove arguments. + __ popl(EDX); + __ popl(EAX); + __ cmpl(EAX, EDX); + if (node->kind() == Token::kEQ) { + __ j(EQUAL, &load_true, Assembler::kNearJump); + } else { + __ j(NOT_EQUAL, &load_true, Assembler::kNearJump); + } + __ LoadObject(EAX, bool_false); + __ jmp(&null_done, Assembler::kNearJump); + __ Bind(&load_true); + __ LoadObject(EAX, bool_true); + __ jmp(&null_done, Assembler::kNearJump); + __ Bind(&non_null_compare); + } + // Do '==' first then negate if necessary, + const String& operator_name = String::ZoneHandle(String::NewSymbol("==")); + const int kNumberOfArguments = 2; + GenerateInstanceCall(node->id(), + node->token_index(), + operator_name, + kNumberOfArguments, + NULL); + + // Result is in EAX. No need to negate if result is not needed. + if ((node->kind() == Token::kNE) && IsResultNeeded(node)) { + // Negate result. + Label load_true, done; + __ LoadObject(EDX, bool_false); + __ cmpl(EAX, EDX); + __ j(EQUAL, &load_true, Assembler::kNearJump); + __ movl(EAX, EDX); // false. + __ jmp(&done, Assembler::kNearJump); + __ Bind(&load_true); + __ LoadObject(EAX, bool_true); + __ Bind(&done); + } + __ Bind(&null_done); + // Result is in EAX. + if (IsResultNeeded(node)) { + __ pushl(EAX); + } + return; + } + + // Call operator. + GenerateBinaryOperatorCall(node->id(), node->token_index(), node->Name()); + // Result is in EAX. + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::CountBackwardLoop() { + Label done; + const Function& function = + Function::ZoneHandle(parsed_function_.function().raw()); + __ LoadObject(EAX, function); + __ movl(EBX, FieldAddress(EAX, Function::invocation_counter_offset())); + __ incl(EBX); + if (!FLAG_report_invocation_count) { + // Prevent overflow. + __ cmpl(EBX, Immediate(FLAG_optimization_invocation_threshold)); + __ j(GREATER, &done); + } + __ movl(FieldAddress(EAX, Function::invocation_counter_offset()), EBX); + __ Bind(&done); +} + + +void CodeGenerator::VisitWhileNode(WhileNode* node) { + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + SourceLabel* label = node->label(); + __ Bind(label->continue_label()); + node->condition()->Visit(this); + GenerateConditionTypeCheck(node->condition()->token_index()); + __ popl(EAX); + __ LoadObject(EDX, bool_true); + __ cmpl(EAX, EDX); + __ j(NOT_EQUAL, label->break_label()); + node->body()->Visit(this); + CountBackwardLoop(); + __ jmp(label->continue_label()); + __ Bind(label->break_label()); +} + + +void CodeGenerator::VisitDoWhileNode(DoWhileNode* node) { + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + SourceLabel* label = node->label(); + Label loop; + __ Bind(&loop); + node->body()->Visit(this); + CountBackwardLoop(); + __ Bind(label->continue_label()); + node->condition()->Visit(this); + GenerateConditionTypeCheck(node->condition()->token_index()); + __ popl(EAX); + __ LoadObject(EDX, bool_true); + __ cmpl(EAX, EDX); + __ j(EQUAL, &loop); + __ Bind(label->break_label()); +} + + +void CodeGenerator::VisitForNode(ForNode* node) { + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + node->initializer()->Visit(this); + SourceLabel* label = node->label(); + Label loop; + __ Bind(&loop); + if (node->condition() != NULL) { + node->condition()->Visit(this); + GenerateConditionTypeCheck(node->condition()->token_index()); + __ popl(EAX); + __ LoadObject(EDX, bool_true); + __ cmpl(EAX, EDX); + __ j(NOT_EQUAL, label->break_label()); + } + node->body()->Visit(this); + CountBackwardLoop(); + __ Bind(label->continue_label()); + node->increment()->Visit(this); + __ jmp(&loop); + __ Bind(label->break_label()); +} + + +void CodeGenerator::VisitJumpNode(JumpNode* node) { + SourceLabel* label = node->label(); + + // Generate inlined code for all finally blocks as we may transfer + // control out of the 'try' blocks if any. + for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) { + node->InlinedFinallyNodeAt(i)->Visit(this); + } + + // Unchain the context(s) up to the outer context level of the scope which + // contains the destination label. + ASSERT(label->owner() != NULL); + LocalScope* outer_context_owner = label->owner()->parent(); + ASSERT(outer_context_owner != NULL); + int target_context_level = 0; + if (outer_context_owner->HasContextLevel()) { + target_context_level = outer_context_owner->context_level(); + ASSERT(target_context_level >= 0); + int context_level = state()->context_level(); + ASSERT(context_level >= target_context_level); + while (context_level-- > target_context_level) { + __ movl(CTX, FieldAddress(CTX, Context::parent_offset())); + } + } + + if (node->kind() == Token::kBREAK) { + __ jmp(label->break_label()); + } else { + __ jmp(label->continue_label()); + } +} + + +void CodeGenerator::VisitConditionalExprNode(ConditionalExprNode* node) { + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + Label false_label, done; + node->condition()->Visit(this); + GenerateConditionTypeCheck(node->condition()->token_index()); + __ popl(EAX); + __ LoadObject(EDX, bool_true); + __ cmpl(EAX, EDX); + __ j(NOT_EQUAL, &false_label); + node->true_expr()->Visit(this); + __ jmp(&done); + __ Bind(&false_label); + node->false_expr()->Visit(this); + __ Bind(&done); + if (!IsResultNeeded(node)) { + __ popl(EAX); + } +} + + +void CodeGenerator::VisitSwitchNode(SwitchNode *node) { + SourceLabel* label = node->label(); + node->body()->Visit(this); + __ Bind(label->break_label()); +} + + +void CodeGenerator::VisitCaseNode(CaseNode* node) { + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + Label case_statements, end_case; + + for (int i = 0; i < node->case_expressions()->length(); i++) { + // Load case expression onto stack. + AstNode* case_expr = node->case_expressions()->NodeAt(i); + case_expr->Visit(this); + __ popl(EAX); + __ CompareObject(EAX, bool_true); + // Jump to case clause code if case expression equals switch expression + __ j(EQUAL, &case_statements); + } + // If this case clause contains the default label, fall through to + // case clause code, else skip this clause. + if (!node->contains_default()) { + __ jmp(&end_case); + } + + // If there is a label associated with this case clause, bind it. + if (node->label() != NULL) { + __ Bind(node->label()->continue_label()); + } + + // Generate code for case clause statements. The parser guarantees that + // the code contains a jump, so we should never fall through the end + // of the statements. + __ Bind(&case_statements); + node->statements()->Visit(this); + __ Bind(&end_case); +} + + +void CodeGenerator::VisitIfNode(IfNode* node) { + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + Label false_label; + node->condition()->Visit(this); + GenerateConditionTypeCheck(node->condition()->token_index()); + __ popl(EAX); + __ LoadObject(EDX, bool_true); + __ cmpl(EAX, EDX); + __ j(NOT_EQUAL, &false_label); + node->true_branch()->Visit(this); + if (node->false_branch() != NULL) { + Label done; + __ jmp(&done); + __ Bind(&false_label); + node->false_branch()->Visit(this); + __ Bind(&done); + } else { + __ Bind(&false_label); + } +} + + +// Operators '&&' and '||' are not overloadabled, inline them. +void CodeGenerator::GenerateLogicalAndOrOp(BinaryOpNode* node) { + // Generate true if (left == true) op (right == true), otherwise generate + // false, with op being either || or &&. + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + const Bool& bool_false = Bool::ZoneHandle(Bool::False()); + Label load_false, done; + node->left()->Visit(this); + GenerateConditionTypeCheck(node->left()->token_index()); + __ popl(EAX); + __ LoadObject(EDX, bool_true); + __ cmpl(EAX, EDX); + if (node->kind() == Token::kAND) { + __ j(NOT_EQUAL, &load_false); + } else { + ASSERT(node->kind() == Token::kOR); + __ j(EQUAL, &done); + } + node->right()->Visit(this); + GenerateConditionTypeCheck(node->right()->token_index()); + __ popl(EAX); + __ LoadObject(EDX, bool_true); + __ cmpl(EAX, EDX); + __ j(EQUAL, &done); + __ Bind(&load_false); + __ LoadObject(EAX, bool_false); + __ Bind(&done); + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +// Expect receiver(left operand) and right operand on stack. +// Return result in EAX. +void CodeGenerator::GenerateBinaryOperatorCall(intptr_t node_id, + intptr_t token_index, + const char* name) { + const String& operator_name = String::ZoneHandle(String::NewSymbol(name)); + const int kNumberOfArguments = 2; + GenerateInstanceCall(node_id, + token_index, + operator_name, + kNumberOfArguments, + NULL); +} + + +void CodeGenerator::VisitBinaryOpNode(BinaryOpNode* node) { + if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) { + // Operators "&&" and "||" cannot be overloaded, therefore inline them + // instead of calling the operator. + GenerateLogicalAndOrOp(node); + return; + } + node->left()->Visit(this); + node->right()->Visit(this); + MarkDeoptPoint(node->id(), node->token_index()); + GenerateBinaryOperatorCall(node->id(), node->token_index(), node->Name()); + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitStringConcatNode(StringConcatNode* node) { + const String& cls_name = String::Handle(String::NewSymbol("StringBase")); + const Library& core_lib = Library::Handle( + Isolate::Current()->object_store()->core_library()); + const Class& cls = Class::Handle(core_lib.LookupClass(cls_name)); + ASSERT(!cls.IsNull()); + const String& func_name = String::Handle(String::NewSymbol("_interpolate")); + const int number_of_parameters = 1; + const Function& interpol_func = Function::ZoneHandle( + Resolver::ResolveStatic(cls, func_name, + number_of_parameters, + Array::Handle(), + Resolver::kIsQualified)); + ASSERT(!interpol_func.IsNull()); + + // First try to concatenate and canonicalize the values at compile time. + bool compile_time_interpolation = true; + Array& literals = Array::Handle(Array::New(node->values()->length())); + for (int i = 0; i < node->values()->length(); i++) { + if (node->values()->ElementAt(i)->IsLiteralNode()) { + LiteralNode* lit = node->values()->ElementAt(i)->AsLiteralNode(); + literals.SetAt(i, lit->literal()); + } else { + compile_time_interpolation = false; + break; + } + } + if (compile_time_interpolation) { + if (!IsResultNeeded(node)) { + return; + } + // Build argument array to pass to the interpolation function. + GrowableArray interpolate_arg; + interpolate_arg.Add(&literals); + // Call the interpolation function. + String& concatenated = String::ZoneHandle(); + concatenated ^= DartEntry::InvokeStatic(interpol_func, interpolate_arg); + if (concatenated.IsUnhandledException()) { + ErrorMsg(node->token_index(), + "Exception thrown in CodeGenerator::VisitStringConcatNode"); + } + ASSERT(!concatenated.IsNull()); + concatenated = String::NewSymbol(concatenated); + + __ LoadObject(EAX, concatenated); + __ pushl(EAX); + return; + } + + // Could not concatenate at compile time, generate a call to + // interpolation function. + ArgumentListNode* interpol_arg = new ArgumentListNode(node->token_index()); + interpol_arg->Add(node->values()); + node->values()->Visit(this); + __ LoadObject(ECX, interpol_func); + __ LoadObject(EDX, ArgumentsDescriptor(interpol_arg->length(), + interpol_arg)); + GenerateCall(node->token_index(), &StubCode::CallStaticFunctionLabel()); + __ addl(ESP, Immediate(interpol_arg->length() * kWordSize)); + // Result is in EAX. + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitInstanceCallNode(InstanceCallNode* node) { + const int number_of_arguments = node->arguments()->length() + 1; + // Compute the receiver object and pass it as first argument to call. + node->receiver()->Visit(this); + // Now compute rest of the arguments to the call. + node->arguments()->Visit(this); + // Some method may be inlined using type feedback, therefore this may be a + // deoptimization point. + MarkDeoptPoint(node->id(), node->token_index()); + + GenerateInstanceCall(node->id(), + node->token_index(), + node->function_name(), + number_of_arguments, + node->arguments()); + // Result is in EAX. + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitStaticCallNode(StaticCallNode* node) { + node->arguments()->Visit(this); + __ LoadObject(ECX, node->function()); + __ LoadObject(EDX, ArgumentsDescriptor(node->arguments()->length(), + node->arguments())); + GenerateCall(node->token_index(), &StubCode::CallStaticFunctionLabel()); + __ addl(ESP, Immediate(node->arguments()->length() * kWordSize)); + // Result is in EAX. + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +void CodeGenerator::VisitClosureCallNode(ClosureCallNode* node) { + // TODO(regis): Does the spec mention if the closure is evaluated before or + // after the arguments? We evaluate it first, i.e. left to right. + // Preserve the current context, since it will be overridden by the closure + // context during the call. + __ pushl(CTX); + // Compute the closure object and pass it as first argument to the stub. + node->closure()->Visit(this); + // Now compute the arguments to the call. + node->arguments()->Visit(this); + // Set up the number of arguments (excluding the closure) to the ClosureCall + // stub which will setup the closure context and jump to the entrypoint of the + // closure function (the function will be compiled if it has not already been + // compiled). + // NOTE: The stub accesses the closure before the parameter list. + __ LoadObject(EDX, ArgumentsDescriptor(node->arguments()->length(), + node->arguments())); + GenerateCall(node->token_index(), &StubCode::CallClosureFunctionLabel()); + __ addl(ESP, Immediate((node->arguments()->length() + 1) * kWordSize)); + // Restore the context. + __ popl(CTX); + // Result is in EAX. + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +// Pushes the type arguments on the stack in preparation of a constructor or +// factory call. +// For a factory call, instantiates (possibly requiring an additional run time +// call) and pushes the type argument vector that will be passed as implicit +// first parameter to the factory. +// For a constructor call allocating an object of a parameterized class, pushes +// the type arguments and the type arguments of the instantiator, without ever +// generating an additional run time call. +// Does nothing for a constructor call allocating an object of a non +// parameterized class. +// Note that a class without proper type parameters may still be parameterized, +// e.g. class A extends Array. +void CodeGenerator::GenerateTypeArguments(ConstructorCallNode* node, + bool is_cls_parameterized) { + // Instantiate the type arguments if necessary. + if (node->type_arguments().IsNull() || + node->type_arguments().IsInstantiated()) { + if (node->constructor().IsFactory() || is_cls_parameterized) { + // A factory requires the type arguments as first parameter. + __ PushObject(node->type_arguments()); + if (!node->constructor().IsFactory()) { + // The allocator additionally requires the instantiator type arguments. + __ PushObject(TypeArguments::ZoneHandle()); // Null instantiator. + } + } + } else { + // The type arguments are uninstantiated. + ASSERT(parsed_function().instantiator() != NULL); + ASSERT(node->constructor().IsFactory() || is_cls_parameterized); + parsed_function().instantiator()->Visit(this); + __ popl(EAX); // Pop instantiator. + if (!parsed_function().function().IsInFactoryScope()) { + const Class& instantiator_class = + Class::Handle(parsed_function().function().owner()); + // The instantiator is the receiver of the caller, which is not a factory. + // The receiver cannot be null; extract its TypeArguments object. + // Note that in the factory case, the instantiator is the first parameter + // of the factory, i.e. already a TypeArguments object. + intptr_t type_arguments_instance_field_offset = + instantiator_class.type_arguments_instance_field_offset(); + ASSERT(type_arguments_instance_field_offset != Class::kNoTypeArguments); + __ movl(EAX, FieldAddress(EAX, type_arguments_instance_field_offset)); + } + // EAX is the instantiator TypeArguments object (or null). + // If EAX is null, no need to instantiate the type arguments, use null, and + // allocate an object of a raw type. + Label type_arguments_instantiated, type_arguments_uninstantiated; + const Immediate raw_null = + Immediate(reinterpret_cast(Object::null())); + __ cmpl(EAX, raw_null); + __ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump); + + // Instantiate non-null type arguments. + if (node->type_arguments().IsUninstantiatedIdentity()) { + // Check if the instantiator type argument vector is a TypeArray of a + // matching length and, if so, use it as the instantiated type_arguments. + __ LoadObject(ECX, Class::ZoneHandle(Object::type_array_class())); + __ cmpl(ECX, FieldAddress(EAX, Object::class_offset())); + __ j(NOT_EQUAL, &type_arguments_uninstantiated, Assembler::kNearJump); + Immediate arguments_length = Immediate(reinterpret_cast( + Smi::New(node->type_arguments().Length()))); + __ cmpl(FieldAddress(EAX, TypeArray::length_offset()), arguments_length); + __ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump); + } + __ Bind(&type_arguments_uninstantiated); + if (node->constructor().IsFactory()) { + // A runtime call to instantiate the type arguments is required before + // calling the factory. + const Object& result = Object::ZoneHandle(); + __ PushObject(result); // Make room for the result of the runtime call. + __ PushObject(node->type_arguments()); + __ pushl(EAX); // Push instantiator type arguments. + GenerateCallRuntime(node->token_index(), + kInstantiateTypeArgumentsRuntimeEntry); + __ popl(EAX); // Pop instantiator type arguments. + __ popl(EAX); // Pop uninstantiated type arguments. + __ popl(EAX); // Pop instantiated type arguments. + __ Bind(&type_arguments_instantiated); + __ pushl(EAX); // Instantiated type arguments. + } else { + // In the non-factory case, we rely on the allocation stub to + // instantiate the type arguments. + __ PushObject(node->type_arguments()); + __ pushl(EAX); // Instantiator type arguments. + Label type_arguments_pushed; + __ jmp(&type_arguments_pushed, Assembler::kNearJump); + + __ Bind(&type_arguments_instantiated); + __ pushl(EAX); // Instantiated type arguments. + __ PushObject(TypeArguments::ZoneHandle()); // Null instantiator. + __ Bind(&type_arguments_pushed); + } + } +} + + +void CodeGenerator::VisitConstructorCallNode(ConstructorCallNode* node) { + const Class& cls = Class::ZoneHandle(node->constructor().owner()); + const bool is_cls_parameterized = cls.IsParameterized(); + GenerateTypeArguments(node, is_cls_parameterized); + if (node->constructor().IsFactory()) { + // The top of stack is an instantiated TypeArguments object (or null). + int num_args = node->arguments()->length() + 1; // +1 to include type args. + node->arguments()->Visit(this); + // Call the factory. + __ LoadObject(ECX, node->constructor()); + __ LoadObject(EDX, ArgumentsDescriptor(num_args, node->arguments())); + GenerateCall(node->token_index(), &StubCode::CallStaticFunctionLabel()); + // Factory constructor returns object in EAX. + __ addl(ESP, Immediate(num_args * kWordSize)); + if (IsResultNeeded(node)) { + __ pushl(EAX); + } + return; + } + + // If cls is parameterized, the type arguments and the instantiator's + // type arguments are on the stack. + const Code& stub = Code::Handle(StubCode::GetAllocationStubForClass(cls)); + const ExternalLabel label(cls.ToCString(), stub.EntryPoint()); + GenerateCall(node->token_index(), &label); + if (is_cls_parameterized) { + __ popl(ECX); // Pop type arguments. + __ popl(ECX); // Pop instantiator type arguments. + } + + if (IsResultNeeded(node)) { + __ pushl(EAX); // Set up return value from allocate. + } + __ pushl(EAX); // First argument(this) for constructor call which follows. + + // Now setup rest of the arguments for the constructor call. + node->arguments()->Visit(this); + + // Call the constructor. + int num_args = node->arguments()->length() + 1; // +1 to include receiver. + __ LoadObject(ECX, node->constructor()); + __ LoadObject(EDX, ArgumentsDescriptor(num_args, node->arguments())); + GenerateCall(node->token_index(), &StubCode::CallStaticFunctionLabel()); + // Constructors do not return any value. + + // Pop out all the other arguments on the stack. + __ addl(ESP, Immediate(num_args * kWordSize)); +} + + +// Expects receiver on stack, returns result in EAX.. +void CodeGenerator::GenerateInstanceGetterCall(intptr_t node_id, + intptr_t token_index, + const String& field_name) { + const String& getter_name = String::ZoneHandle(Field::GetterName(field_name)); + const int kNumberOfArguments = 1; + GenerateInstanceCall(node_id, + token_index, + getter_name, + kNumberOfArguments, + NULL); +} + + +// Call to the instance getter. +void CodeGenerator::VisitInstanceGetterNode(InstanceGetterNode* node) { + node->receiver()->Visit(this); + MarkDeoptPoint(node->id(), node->token_index()); + GenerateInstanceGetterCall(node->id(), + node->token_index(), + node->field_name()); + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +// Expects receiver and value on stack. +void CodeGenerator::GenerateInstanceSetterCall(intptr_t node_id, + intptr_t token_index, + const String& field_name) { + const String& setter_name = String::ZoneHandle(Field::SetterName(field_name)); + const int kNumberOfArguments = 2; // receiver + value. + GenerateInstanceCall(node_id, + token_index, + setter_name, + kNumberOfArguments, + NULL); +} + + +// The call to the instance setter implements the assignment to a field. +// The result of the assignment to a field is the value being stored. +void CodeGenerator::VisitInstanceSetterNode(InstanceSetterNode* node) { + // Compute the receiver object and pass it as first argument to call. + node->receiver()->Visit(this); + node->value()->Visit(this); + MarkDeoptPoint(node->id(), node->token_index()); + if (IsResultNeeded(node)) { + __ popl(EAX); // value. + __ popl(EDX); // receiver. + __ pushl(EAX); // Preserve value. + __ pushl(EDX); // arg0: receiver. + __ pushl(EAX); // arg1: value. + } + // It is not necessary to generate a type test of the assigned value here, + // because the setter will check the type of its incoming arguments. + GenerateInstanceSetterCall(node->id(), + node->token_index(), + node->field_name()); +} + + +// Return result in EAX. +void CodeGenerator::GenerateStaticGetterCall(intptr_t token_index, + const Class& field_class, + const String& field_name) { + const String& getter_name = String::Handle(Field::GetterName(field_name)); + const Function& function = + Function::ZoneHandle(field_class.LookupStaticFunction(getter_name)); + if (function.IsNull()) { + ErrorMsg(token_index, "Static getter does not exist: %s", + getter_name.ToCString()); + } + __ LoadObject(ECX, function); + const int kNumberOfArguments = 0; + __ LoadObject(EDX, ArgumentsDescriptor(kNumberOfArguments, NULL)); + GenerateCall(token_index, &StubCode::CallStaticFunctionLabel()); + // No arguments were pushed, hence nothing to pop. +} + + +// Call to static getter. +void CodeGenerator::VisitStaticGetterNode(StaticGetterNode* node) { + GenerateStaticGetterCall(node->token_index(), + node->cls(), + node->field_name()); + // Result is in EAX. + if (IsResultNeeded(node)) { + __ pushl(EAX); + } +} + + +// Expects value on stack. +void CodeGenerator::GenerateStaticSetterCall(intptr_t token_index, + const Class& field_class, + const String& field_name) { + const String& setter_name = String::Handle(Field::SetterName(field_name)); + const Function& function = + Function::ZoneHandle(field_class.LookupStaticFunction(setter_name)); + __ LoadObject(ECX, function); + const int kNumberOfArguments = 1; // value. + __ LoadObject(EDX, ArgumentsDescriptor(kNumberOfArguments, NULL)); + GenerateCall(token_index, &StubCode::CallStaticFunctionLabel()); + __ addl(ESP, Immediate(kNumberOfArguments * kWordSize)); +} + + +// The call to static setter implements assignment to a static field. +// The result of the assignment is the value being stored. +void CodeGenerator::VisitStaticSetterNode(StaticSetterNode* node) { + node->value()->Visit(this); + if (IsResultNeeded(node)) { + // Preserve the original value when returning from setter. + __ movl(EAX, Address(ESP, 0)); + __ pushl(EAX); // arg0: value. + } + // It is not necessary to generate a type test of the assigned value here, + // because the setter will check the type of its incoming arguments. + GenerateStaticSetterCall(node->token_index(), + node->cls(), + node->field_name()); +} + + +void CodeGenerator::VisitNativeBodyNode(NativeBodyNode* node) { + // Push the result place holder initialized to NULL. + __ PushObject(Object::ZoneHandle()); + // Pass a pointer to the first argument in EAX. + if (!node->has_optional_parameters()) { + __ leal(EAX, Address(EBP, (1 + node->argument_count()) * kWordSize)); + } else { + __ leal(EAX, Address(EBP, -1 * kWordSize)); + } + __ movl(ECX, Immediate(reinterpret_cast(node->native_c_function()))); + __ movl(EDX, Immediate(node->argument_count())); + GenerateCall(node->token_index(), &StubCode::CallNativeCFunctionLabel()); + // Result is on the stack. + if (!IsResultNeeded(node)) { + __ popl(EAX); + } +} + + +void CodeGenerator::VisitCatchClauseNode(CatchClauseNode* node) { + // NOTE: The implicit variables ':saved_context', ':exception_var' + // and ':stacktrace_var' can never be captured variables. + // Restore CTX from local variable ':saved_context'. + GenerateLoadVariable(CTX, node->context_var()); + + // Restore ESP from EBP as we are coming from a throw and the code for + // popping arguments has not been run. + ASSERT(locals_space_size() >= 0); + __ movl(ESP, EBP); + __ subl(ESP, Immediate(locals_space_size())); + + // The JumpToExceptionHandler trampoline code sets up + // - the exception object in EAX (kExceptionObjectReg) + // - the stacktrace object in register EDX (kStackTraceObjectReg) + // We now setup the exception object and the trace object + // so that the handler code has access to these objects. + GenerateStoreVariable(node->exception_var(), + kExceptionObjectReg, + kNoRegister); + GenerateStoreVariable(node->stacktrace_var(), + kStackTraceObjectReg, + kNoRegister); + + // Now generate code for the catch handler block. + node->VisitChildren(this); +} + + +void CodeGenerator::VisitTryCatchNode(TryCatchNode* node) { + CodeGeneratorState codegen_state(this); + int outer_try_index = state()->try_index(); + // We are about to generate code for a new try block, generate an + // unique 'try index' for this block and set that try index in + // the code generator state. + int try_index = generate_next_try_index(); + state()->set_try_index(try_index); + exception_handlers_list_->AddHandler(try_index, -1); + + // Preserve CTX into local variable '%saved_context'. + GenerateStoreVariable(node->context_var(), CTX, kNoRegister); + + node->try_block()->Visit(this); + + // We are done generating code for the try block. + ASSERT(state()->try_index() > CatchClauseNode::kInvalidTryIndex); + ASSERT(try_index == state()->try_index()); + state()->set_try_index(outer_try_index); + + CatchClauseNode* catch_block = node->catch_block(); + if (catch_block != NULL) { + // Jump over the catch handler block, when exceptions are thrown we + // will end up at the next instruction. + __ jmp(node->end_catch_label()->continue_label()); + + // Set the corresponding try index for this catch block so + // that we can set the appropriate handler pc when we generate + // code for this catch block. + catch_block->set_try_index(try_index); + + // Set the handler pc for this try index in the exception handler + // table. + exception_handlers_list_->SetPcOffset(try_index, assembler_->CodeSize()); + + // Generate code for the catch block. + catch_block->Visit(this); + + // Bind the end of catch blocks label here. + __ Bind(node->end_catch_label()->continue_label()); + } + + // Generate code for the finally block if one exists. + if (node->finally_block() != NULL) { + node->finally_block()->Visit(this); + } +} + + +void CodeGenerator::VisitThrowNode(ThrowNode* node) { + const Object& result = Object::ZoneHandle(); + node->exception()->Visit(this); + __ popl(EAX); // Exception object is now in EAX. + if (node->stacktrace() != NULL) { + __ PushObject(result); // Make room for the result of the runtime call. + __ pushl(EAX); // Push the exception object. + node->stacktrace()->Visit(this); + GenerateCallRuntime(node->token_index(), kReThrowRuntimeEntry); + } else { + __ PushObject(result); // Make room for the result of the runtime call. + __ pushl(EAX); // Push the exception object. + GenerateCallRuntime(node->token_index(), kThrowRuntimeEntry); + } + // We should never return here. + __ int3(); +} + + +void CodeGenerator::VisitInlinedFinallyNode(InlinedFinallyNode* node) { + int try_index = state()->try_index(); + if (try_index >= 0) { + // We are about to generate code for an inlined finally block. Exceptions + // thrown in this block of code should be treated as though they are + // thrown not from the current try block but the outer try block if any. + // the code generator state. + state()->set_try_index((try_index - 1)); + } + + // Restore CTX from local variable ':saved_context'. + GenerateLoadVariable(CTX, node->context_var()); + node->finally_block()->Visit(this); + + if (try_index >= 0) { + state()->set_try_index(try_index); + } +} + + +void CodeGenerator::GenerateCall(intptr_t token_index, + const ExternalLabel* ext_label) { + __ call(ext_label); + AddCurrentDescriptor(PcDescriptors::kOther, AstNode::kInvalidId, token_index); +} + + +void CodeGenerator::GenerateCallRuntime(intptr_t token_index, + const RuntimeEntry& entry) { + __ CallRuntimeFromDart(entry); + AddCurrentDescriptor(PcDescriptors::kOther, AstNode::kInvalidId, token_index); +} + + +void CodeGenerator::MarkDeoptPoint(intptr_t node_id, + intptr_t token_index) { + ASSERT(node_id != AstNode::kInvalidId); + AddCurrentDescriptor(PcDescriptors::kDeopt, node_id, token_index); +} + + +// Uses current pc position and try-index. +void CodeGenerator::AddCurrentDescriptor(PcDescriptors::Kind kind, + intptr_t node_id, + intptr_t token_index) { + pc_descriptors_list_->AddDescriptor(kind, + assembler_->CodeSize(), + node_id, + token_index, + state()->try_index()); +} + + +void CodeGenerator::ErrorMsg(intptr_t token_index, const char* format, ...) { + char error_msg[Parser::kErrorBuflen]; + va_list args; + va_start(args, format); + const Class& cls = Class::Handle(parsed_function_.function().owner()); + const Script& script = Script::Handle(cls.script()); + Parser::ReportMsg(script, token_index, "Error", error_msg, format, args); + Isolate::Current()->long_jump_base()->Jump(1, error_msg); + UNREACHABLE(); +} + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/code_generator_ia32.h b/runtime/vm/code_generator_ia32.h new file mode 100644 index 00000000000..cfece1c94d9 --- /dev/null +++ b/runtime/vm/code_generator_ia32.h @@ -0,0 +1,157 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_CODE_GENERATOR_IA32_H_ +#define VM_CODE_GENERATOR_IA32_H_ + +#ifndef VM_CODE_GENERATOR_H_ +#error Do not include code_generator_ia32.h directly; use code_generator.h. +#endif + +#include "vm/assembler.h" +#include "vm/ast.h" +#include "vm/growable_array.h" +#include "vm/parser.h" + +namespace dart { + +// Forward Declarations. +class Assembler; +class AstNode; +class CodeGeneratorState; +class SourceLabel; + +class CodeGenerator : public AstNodeVisitor { + public: + CodeGenerator(Assembler* assembler, const ParsedFunction& parsed_function); + virtual ~CodeGenerator() { } + + // Accessors. + Assembler* assembler() const { return assembler_; } + + const ParsedFunction& parsed_function() const { return parsed_function_; } + + void GenerateCode(); + virtual void GenerateDeferredCode(); + +#define DEFINE_VISITOR_FUNCTION(type, name) \ + virtual void Visit##type(type* node); +NODE_LIST(DEFINE_VISITOR_FUNCTION) +#undef DEFINE_VISITOR_FUNCTION + + CodeGeneratorState* state() const { return state_; } + void set_state(CodeGeneratorState* state) { state_ = state; } + + // Add exception handler table to code. + void FinalizeExceptionHandlers(const Code& code); + + // Add pc descriptors to code. + void FinalizePcDescriptors(const Code& code); + + // Allocate and return an arguments descriptor for the given 'num_arguments' + // and 'arguments'. If 'arguments' is NULL, treat all 'num_arguments' as + // positional arguments. + static const Array& ArgumentsDescriptor(int num_arguments, + ArgumentListNode* arguments); + + virtual bool IsOptimizing() const { + return false; + } + + virtual void CountBackwardLoop(); + + private: + // TODO(srdjan): Remove the friendship once the two compilers are properly + // structured. + friend class OptimizingCodeGenerator; + + // Forward Declarations. + class DescriptorList; + class HandlerList; + + // Return true if intrinsification was completed and no other code + // needs to be generated. + virtual bool TryIntrinsify() { return false; } + virtual void GeneratePreEntryCode(); + void GenerateLegacyEntryCode(); + void GenerateEntryCode(); + void GenerateLoadVariable(Register dst, const LocalVariable& local); + void GeneratePushVariable(const LocalVariable& variable, Register scratch); + void GenerateStoreVariable(const LocalVariable& local, + Register src, + Register scratch); + void GenerateLogicalNotOp(UnaryOpNode* node); + void GenerateLogicalAndOrOp(BinaryOpNode* node); + void GenerateInstanceGetterCall(intptr_t node_id, + intptr_t token_index, + const String& field_name); + void GenerateInstanceSetterCall(intptr_t node_id, + intptr_t token_index, + const String& field_name); + void GenerateBinaryOperatorCall(intptr_t node_id, + intptr_t token_index, + const char* operator_name); + void GenerateStaticGetterCall(intptr_t token_index, + const Class& field_class, + const String& field_name); + void GenerateStaticSetterCall(intptr_t token_index, + const Class& field_class, + const String& field_name); + void GenerateLoadIndexed(intptr_t node_id, intptr_t token_index); + void GenerateStoreIndexed(intptr_t node_id, + intptr_t token_index, + bool preserve_value); + + void GenerateInstanceCall(intptr_t node_id, + intptr_t token_index, + const String& function_name, + int arg_count, + ArgumentListNode* arguments); + + void GenerateInstanceOf(intptr_t token_index, + const Type& type, + bool negate_result); + void GenerateAssertAssignable(intptr_t token_index, + const Type& dst_type, + const String& dst_name); + void GenerateArgumentTypeChecks(); + void GenerateConditionTypeCheck(intptr_t token_index); + + void GenerateTypeArguments(ConstructorCallNode* node, + bool is_cls_parameterized); + + intptr_t locals_space_size() const { return locals_space_size_; } + void set_locals_space_size(intptr_t value) { locals_space_size_ = value; } + + bool IsResultNeeded(AstNode* node) const; + + void GenerateCall(intptr_t token_index, const ExternalLabel* ext_label); + void GenerateCallRuntime(intptr_t token_index, const RuntimeEntry& entry); + + void GenerateInlinedFinallyBlocks(SourceLabel* label); + + void ErrorMsg(intptr_t token_index, const char* format, ...); + + int generate_next_try_index() { return try_index_ += 1; } + + void MarkDeoptPoint(intptr_t node_id, intptr_t token_index); + void AddCurrentDescriptor(PcDescriptors::Kind kind, + intptr_t node_id, + intptr_t token_index); + + Assembler* assembler_; + const ParsedFunction& parsed_function_; + intptr_t locals_space_size_; + CodeGeneratorState* state_; + DescriptorList* pc_descriptors_list_; + HandlerList* exception_handlers_list_; + int try_index_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(CodeGenerator); +}; + + +} // namespace dart + +#endif // VM_CODE_GENERATOR_IA32_H_ diff --git a/runtime/vm/code_generator_ia32_test.cc b/runtime/vm/code_generator_ia32_test.cc new file mode 100644 index 00000000000..854594023ad --- /dev/null +++ b/runtime/vm/code_generator_ia32_test.cc @@ -0,0 +1,499 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" +#if defined(TARGET_ARCH_IA32) + +#include "vm/code_generator.h" + +#include "vm/ast.h" +#include "vm/assembler.h" +#include "vm/assert.h" +#include "vm/class_finalizer.h" +#include "vm/compiler.h" +#include "vm/dart_entry.h" +#include "vm/native_entry.h" +#include "vm/native_entry_test.h" +#include "vm/unit_test.h" +#include "vm/virtual_memory.h" + +namespace dart { + +#define __ assembler_-> + + +static const intptr_t kPos = 1; // Dummy token index in non-existing source. + + +// Helper to allocate and return a LocalVariable. +static LocalVariable* NewTestLocalVariable(const char* name) { + const String& variable_name = String::ZoneHandle(String::New(name)); + const Type& variable_type = Type::ZoneHandle(Type::VarType()); + return new LocalVariable(kPos, variable_name, variable_type); +} + + +CODEGEN_TEST_GENERATE(SimpleReturnCodegen, test) { + test->node_sequence()->Add(new ReturnNode(kPos)); +} +CODEGEN_TEST_RUN(SimpleReturnCodegen, Instance::null()) + + +CODEGEN_TEST_GENERATE(SmiReturnCodegen, test) { + LiteralNode* l = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3))); + test->node_sequence()->Add(new ReturnNode(kPos, l)); +} +CODEGEN_TEST_RUN(SmiReturnCodegen, Smi::New(3)) + + +CODEGEN_TEST2_GENERATE(SimpleStaticCallCodegen, function, test) { + // Wrap the SmiReturnCodegen test above as a static function and call it. + ArgumentListNode* no_arguments = new ArgumentListNode(kPos); + test->node_sequence()->Add( + new ReturnNode(kPos, new StaticCallNode(kPos, function, no_arguments))); +} +CODEGEN_TEST2_RUN(SimpleStaticCallCodegen, SmiReturnCodegen, Smi::New(3)) + + +CODEGEN_TEST_GENERATE(ReturnParameterCodegen, test) { + SequenceNode* node_seq = test->node_sequence(); + const int num_params = 1; + LocalVariable* parameter = NewTestLocalVariable("parameter"); + LocalScope* local_scope = node_seq->scope(); + local_scope->AddVariable(parameter); + ASSERT(local_scope->num_variables() == num_params); + const Function& function = test->function(); + function.set_num_fixed_parameters(num_params); + ASSERT(function.num_optional_parameters() == 0); + node_seq->Add(new ReturnNode(kPos, new LoadLocalNode(kPos, *parameter))); +} + + +CODEGEN_TEST2_GENERATE(StaticCallReturnParameterCodegen, function, test) { + // Wrap and call the ReturnParameterCodegen test above as a static function. + SequenceNode* node_seq = test->node_sequence(); + ArgumentListNode* arguments = new ArgumentListNode(kPos); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3)))); + node_seq->Add(new ReturnNode(kPos, + new StaticCallNode(kPos, function, arguments))); +} +CODEGEN_TEST2_RUN(StaticCallReturnParameterCodegen, + ReturnParameterCodegen, + Smi::New(3)) + + +CODEGEN_TEST_GENERATE(SmiParamSumCodegen, test) { + SequenceNode* node_seq = test->node_sequence(); + const int num_params = 2; + LocalVariable* param1 = NewTestLocalVariable("param1"); + LocalVariable* param2 = NewTestLocalVariable("param2"); + const int num_locals = 1; + LocalVariable* sum = NewTestLocalVariable("sum"); + LocalScope* local_scope = node_seq->scope(); + local_scope->AddVariable(param1); + local_scope->AddVariable(param2); + local_scope->AddVariable(sum); + ASSERT(local_scope->num_variables() == num_params + num_locals); + const Function& function = test->function(); + function.set_num_fixed_parameters(num_params); + ASSERT(function.num_optional_parameters() == 0); + BinaryOpNode* add = new BinaryOpNode(kPos, + Token::kADD, + new LoadLocalNode(kPos, *param1), + new LoadLocalNode(kPos, *param2)); + node_seq->Add(new StoreLocalNode(kPos, *sum, add)); + node_seq->Add(new ReturnNode(kPos, new LoadLocalNode(kPos, *sum))); +} + + +CODEGEN_TEST2_GENERATE(StaticCallSmiParamSumCodegen, function, test) { + // Wrap and call the SmiParamSumCodegen test above as a static function. + SequenceNode* node_seq = test->node_sequence(); + ArgumentListNode* arguments = new ArgumentListNode(kPos); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3)))); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2)))); + node_seq->Add(new ReturnNode(kPos, + new StaticCallNode(kPos, function, arguments))); +} +CODEGEN_TEST2_RUN(StaticCallSmiParamSumCodegen, + SmiParamSumCodegen, + Smi::New(5)) + + +CODEGEN_TEST_GENERATE(SmiAddCodegen, test) { + SequenceNode* node_seq = test->node_sequence(); + LiteralNode* a = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3))); + LiteralNode* b = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2))); + BinaryOpNode* add_node = new BinaryOpNode(kPos, Token::kADD, a, b); + node_seq->Add(new ReturnNode(kPos, add_node)); +} +CODEGEN_TEST_RUN(SmiAddCodegen, Smi::New(5)) + + +CODEGEN_TEST_GENERATE(GenericAddCodegen, test) { + SequenceNode* node_seq = test->node_sequence(); + LiteralNode* a = new LiteralNode(kPos, Double::ZoneHandle(Double::New(12.2))); + LiteralNode* b = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2))); + BinaryOpNode* add_node_1 = new BinaryOpNode(kPos, Token::kADD, a, b); + LiteralNode* c = new LiteralNode(kPos, Double::ZoneHandle(Double::New(0.8))); + BinaryOpNode* add_node_2 = new BinaryOpNode(kPos, Token::kADD, add_node_1, c); + node_seq->Add(new ReturnNode(kPos, add_node_2)); +} +CODEGEN_TEST_RUN(GenericAddCodegen, Double::New(15.0)) + + +CODEGEN_TEST_GENERATE(SmiBinaryOpCodegen, test) { + SequenceNode* node_seq = test->node_sequence(); + LiteralNode* a = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(4))); + LiteralNode* b = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2))); + LiteralNode* c = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3))); + BinaryOpNode* sub_node = + new BinaryOpNode(kPos, Token::kSUB, a, b); // 4 - 2 -> 2. + BinaryOpNode* mul_node = + new BinaryOpNode(kPos, Token::kMUL, sub_node, c); // 2 * 3 -> 6. + BinaryOpNode* div_node = + new BinaryOpNode(kPos, Token::kTRUNCDIV, mul_node, b); // 6 ~/ 2 -> 3. + node_seq->Add(new ReturnNode(kPos, div_node)); +} +CODEGEN_TEST_RUN(SmiBinaryOpCodegen, Smi::New(3)) + + +CODEGEN_TEST_GENERATE(BoolNotCodegen, test) { + SequenceNode* node_seq = test->node_sequence(); + const Bool& bool_false = Bool::ZoneHandle(Bool::False()); + LiteralNode* b = new LiteralNode(kPos, bool_false); + UnaryOpNode* not_node = new UnaryOpNode(kPos, Token::kNOT, b); + node_seq->Add(new ReturnNode(kPos, not_node)); +} +CODEGEN_TEST_RUN(BoolNotCodegen, Bool::True()) + + +CODEGEN_TEST_GENERATE(BoolAndCodegen, test) { + SequenceNode* node_seq = test->node_sequence(); + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + const Bool& bool_false = Bool::ZoneHandle(Bool::False()); + LiteralNode* a = new LiteralNode(kPos, bool_true); + LiteralNode* b = new LiteralNode(kPos, bool_false); + BinaryOpNode* and_node = new BinaryOpNode(kPos, Token::kAND, a, b); + node_seq->Add(new ReturnNode(kPos, and_node)); +} +CODEGEN_TEST_RUN(BoolAndCodegen, Bool::False()) + + +CODEGEN_TEST_GENERATE(BinaryOpCodegen, test) { + SequenceNode* node_seq = test->node_sequence(); + LiteralNode* a = new LiteralNode(kPos, Double::ZoneHandle(Double::New(12))); + LiteralNode* b = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2))); + LiteralNode* c = new LiteralNode(kPos, Double::ZoneHandle(Double::New(0.5))); + BinaryOpNode* sub_node = new BinaryOpNode(kPos, Token::kSUB, a, b); + BinaryOpNode* mul_node = new BinaryOpNode(kPos, Token::kMUL, sub_node, c); + BinaryOpNode* div_node = new BinaryOpNode(kPos, Token::kDIV, mul_node, b); + node_seq->Add(new ReturnNode(kPos, div_node)); +} +CODEGEN_TEST_RUN(BinaryOpCodegen, Double::New(2.5)); + + +// Tested Dart code: +// int dec(int a, int b = 1) native: "TestSmiSub"; +// The native entry TestSmiSub implements dec natively. +CODEGEN_TEST_GENERATE(NativeDecCodegen, test) { + // A NativeBodyNode, preceded by an EnterNode and followed by a ReturnNode, + // implements the body of a native Dart function. Let's take this native + // function as an example: int dec(int a, int b = 1) native; + // Since this function has an optional parameter, its prologue will copy + // incoming parameters to locals. + SequenceNode* node_seq = test->node_sequence(); + const int num_fixed_params = 1; + const int num_opt_params = 1; + const int num_params = num_fixed_params + num_opt_params; + LocalScope* local_scope = node_seq->scope(); + local_scope->AddVariable(NewTestLocalVariable("a")); + local_scope->AddVariable(NewTestLocalVariable("b")); + ASSERT(local_scope->num_variables() == num_params); + const Array& default_values = Array::ZoneHandle(Array::New(num_opt_params)); + default_values.SetAt(0, Smi::ZoneHandle(Smi::New(1))); // b = 1. + test->set_default_parameter_values(default_values); + const Function& function = test->function(); + function.set_num_fixed_parameters(num_fixed_params); + function.set_num_optional_parameters(num_opt_params); + const bool has_opt_params = true; + const String& native_name = + String::ZoneHandle(String::NewSymbol("TestSmiSub")); + NativeFunction native_function = + reinterpret_cast(NATIVE_ENTRY_FUNCTION(TestSmiSub)); + node_seq->Add(new ReturnNode(kPos, + new NativeBodyNode(kPos, + native_name, + native_function, + num_params, + has_opt_params))); +} + + +// Tested Dart code: +// return dec(5); +CODEGEN_TEST2_GENERATE(StaticDecCallCodegen, function, test) { + SequenceNode* node_seq = test->node_sequence(); + ArgumentListNode* arguments = new ArgumentListNode(kPos); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(5)))); + node_seq->Add(new ReturnNode(kPos, + new StaticCallNode(kPos, function, arguments))); +} +CODEGEN_TEST2_RUN(StaticDecCallCodegen, NativeDecCodegen, Smi::New(4)) + + +CODEGEN_TEST_GENERATE(SmiUnaryOpCodegen, test) { + SequenceNode* node_seq = test->node_sequence(); + LiteralNode* a = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(12))); + UnaryOpNode* neg_node = new UnaryOpNode(kPos, Token::kSUB, a); + node_seq->Add(new ReturnNode(kPos, neg_node)); +} +CODEGEN_TEST_RUN(SmiUnaryOpCodegen, Smi::New(-12)) + + +CODEGEN_TEST_GENERATE(DoubleUnaryOpCodegen, test) { + SequenceNode* node_seq = test->node_sequence(); + LiteralNode* a = new LiteralNode(kPos, Double::ZoneHandle(Double::New(12.0))); + UnaryOpNode* neg_node = new UnaryOpNode(kPos, Token::kSUB, a); + node_seq->Add(new ReturnNode(kPos, neg_node)); +} +CODEGEN_TEST_RUN(DoubleUnaryOpCodegen, Double::New(-12.0)) + + +static Library& MakeTestLibrary(const char* url) { + const String& lib_url = String::ZoneHandle(String::NewSymbol(url)); + Library& lib = Library::ZoneHandle(Library::New(lib_url)); + lib.Register(); + return lib; +} + + +static RawClass* LookupClass(const Library& lib, const char* name) { + const String& cls_name = String::ZoneHandle(String::NewSymbol(name)); + return lib.LookupClass(cls_name); +} + + +CODEGEN_TEST_GENERATE(StaticCallCodegen, test) { + const char* kScriptChars = + "class A {\n" + " static bar() { return 42; }\n" + " static fly() { return 5; }\n" + "}\n"; + + String& url = String::Handle(String::New("dart-test:CompileScript")); + String& source = String::Handle(String::New(kScriptChars)); + Script& script = Script::Handle(Script::New(url, source, RawScript::kSource)); + Library& lib = MakeTestLibrary("TestLib"); + EXPECT(CompilerTest::TestCompileScript(lib, script)); + Class& cls = Class::Handle(LookupClass(lib, "A")); + EXPECT(!cls.IsNull()); + + // 'bar' will not be compiled. + String& function_bar_name = String::Handle(String::New("bar")); + Function& function_bar = + Function::ZoneHandle(cls.LookupStaticFunction(function_bar_name)); + EXPECT(!function_bar.IsNull()); + EXPECT(!function_bar.HasCode()); + + // 'fly' will be compiled. + String& function_fly_name = String::Handle(String::New("fly")); + Function& function_fly = + Function::ZoneHandle(cls.LookupStaticFunction(function_fly_name)); + EXPECT(!function_fly.IsNull()); + EXPECT(CompilerTest::TestCompileFunction(function_fly)); + EXPECT(function_fly.HasCode()); + + ArgumentListNode* no_arguments = new ArgumentListNode(kPos); + StaticCallNode* call_bar = + new StaticCallNode(kPos, function_bar, no_arguments); + StaticCallNode* call_fly = + new StaticCallNode(kPos, function_fly, no_arguments); + + BinaryOpNode* add_node = + new BinaryOpNode(kPos, Token::kADD, call_bar, call_fly); + + test->node_sequence()->Add(new ReturnNode(kPos, add_node)); +} +CODEGEN_TEST_RUN(StaticCallCodegen, Smi::New(42 + 5)) + + +CODEGEN_TEST_GENERATE(InstanceCallCodegen, test) { + const char* kScriptChars = + "class A {\n" + " A() {}\n" + " int bar() { return 42; }\n" + "}\n"; + + String& url = String::Handle(String::New("dart-test:CompileScript")); + String& source = String::Handle(String::New(kScriptChars)); + Script& script = Script::Handle(Script::New(url, source, RawScript::kSource)); + Library& lib = MakeTestLibrary("TestLib"); + EXPECT(CompilerTest::TestCompileScript(lib, script)); + EXPECT(ClassFinalizer::FinalizePendingClasses()); + Class& cls = Class::ZoneHandle(LookupClass(lib, "A")); + EXPECT(!cls.IsNull()); + + String& constructor_name = String::Handle(String::New("A.")); + Function& constructor = + Function::ZoneHandle(cls.LookupConstructor(constructor_name)); + EXPECT(!constructor.IsNull()); + + // The unit test creates an instance of class A and calls function 'bar'. + String& function_bar_name = String::ZoneHandle(String::NewSymbol("bar")); + ArgumentListNode* no_arguments = new ArgumentListNode(kPos); + const TypeArguments& no_type_arguments = TypeArguments::ZoneHandle(); + InstanceCallNode* call_bar = new InstanceCallNode( + kPos, + new ConstructorCallNode( + kPos, no_type_arguments, constructor, no_arguments), + function_bar_name, + no_arguments); + + test->node_sequence()->Add(new ReturnNode(kPos, call_bar)); +} +CODEGEN_TEST_RUN(InstanceCallCodegen, Smi::New(42)) + + +// Tested Dart code: +// int sum(int a, int b, +// [int c = 10, int d = 21, int e = -32]) native: "TestSmiSum"; +// The native entry TestSmiSum implements sum natively. +CODEGEN_TEST_GENERATE(NativeSumCodegen, test) { + SequenceNode* node_seq = test->node_sequence(); + const int num_fixed_params = 2; + const int num_opt_params = 3; + const int num_params = num_fixed_params + num_opt_params; + LocalScope* local_scope = node_seq->scope(); + local_scope->AddVariable(NewTestLocalVariable("a")); + local_scope->AddVariable(NewTestLocalVariable("b")); + local_scope->AddVariable(NewTestLocalVariable("c")); + local_scope->AddVariable(NewTestLocalVariable("d")); + local_scope->AddVariable(NewTestLocalVariable("e")); + ASSERT(local_scope->num_variables() == num_params); + const Array& default_values = Array::ZoneHandle(Array::New(num_opt_params)); + default_values.SetAt(0, Smi::ZoneHandle(Smi::New(10))); + default_values.SetAt(1, Smi::ZoneHandle(Smi::New(21))); + default_values.SetAt(2, Smi::ZoneHandle(Smi::New(-32))); + test->set_default_parameter_values(default_values); + const Function& function = test->function(); + function.set_num_fixed_parameters(num_fixed_params); + function.set_num_optional_parameters(num_opt_params); + function.set_parameter_types(Array::Handle(Array::New(num_params))); + function.set_parameter_names(Array::Handle(Array::New(num_params))); + const Type& param_type = Type::Handle(Type::VarType()); + for (int i = 0; i < num_params - 1; i++) { + function.SetParameterTypeAt(i, param_type); + } + const bool has_opt_params = true; + const String& native_name = + String::ZoneHandle(String::NewSymbol("TestSmiSum")); + NativeFunction native_function = + reinterpret_cast(NATIVE_ENTRY_FUNCTION(TestSmiSum)); + node_seq->Add(new ReturnNode(kPos, + new NativeBodyNode(kPos, + native_name, + native_function, + num_params, + has_opt_params))); +} + + +// Tested Dart code, calling function sum declared above: +// return sum(1, 3); +// Optional arguments are not passed and hence are set to their default values. +CODEGEN_TEST2_GENERATE(StaticSumCallNoOptCodegen, function, test) { + SequenceNode* node_seq = test->node_sequence(); + ArgumentListNode* arguments = new ArgumentListNode(kPos); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(1)))); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3)))); + node_seq->Add(new ReturnNode(kPos, + new StaticCallNode(kPos, function, arguments))); +} +CODEGEN_TEST2_RUN(StaticSumCallNoOptCodegen, + NativeSumCodegen, + Smi::New(1 + 3 + 10 + 21 - 32)) + + +// Tested Dart code, calling function sum declared above: +// return sum(1, 3, 5); +// Only one out of three optional arguments is passed in; the second and third +// arguments are hence set to their default values. +CODEGEN_TEST2_GENERATE(StaticSumCallOneOptCodegen, function, test) { + SequenceNode* node_seq = test->node_sequence(); + ArgumentListNode* arguments = new ArgumentListNode(kPos); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(1)))); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3)))); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(5)))); + node_seq->Add(new ReturnNode(kPos, + new StaticCallNode(kPos, function, arguments))); +} +CODEGEN_TEST2_RUN(StaticSumCallOneOptCodegen, + NativeSumCodegen, + Smi::New(1 + 3 + 5 + 21 - 32)) + + +// Tested Dart code, calling function sum declared above: +// return sum(0, 1, 1, 2, 3); +// Optional arguments are passed in. +CODEGEN_TEST2_GENERATE(StaticSumCallTenFiboCodegen, function, test) { + SequenceNode* node_seq = test->node_sequence(); + ArgumentListNode* arguments = new ArgumentListNode(kPos); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(0)))); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(1)))); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(1)))); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2)))); + arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3)))); + node_seq->Add(new ReturnNode(kPos, + new StaticCallNode(kPos, function, arguments))); +} +CODEGEN_TEST2_RUN( + StaticSumCallTenFiboCodegen, + NativeSumCodegen, + Smi::New(0 + 1 + 1 + 2 + 3)) + + +// Test allocation of dart objects. +CODEGEN_TEST_GENERATE(AllocateNewObjectCodegen, test) { + const char* kScriptChars = + "class A {\n" + " A() {}\n" + " static bar() { return 42; }\n" + "}\n"; + + String& url = String::Handle(String::New("dart-test:CompileScript")); + String& source = String::Handle(String::New(kScriptChars)); + Script& script = Script::Handle(Script::New(url, source, RawScript::kSource)); + Library& lib = MakeTestLibrary("TestLib"); + EXPECT(CompilerTest::TestCompileScript(lib, script)); + EXPECT(ClassFinalizer::FinalizePendingClasses()); + Class& cls = Class::ZoneHandle(LookupClass(lib, "A")); + EXPECT(!cls.IsNull()); + + String& constructor_name = String::Handle(String::New("A.")); + Function& constructor = + Function::ZoneHandle(cls.LookupConstructor(constructor_name)); + EXPECT(!constructor.IsNull()); + + const TypeArguments& no_type_arguments = TypeArguments::ZoneHandle(); + ArgumentListNode* no_arguments = new ArgumentListNode(kPos); + test->node_sequence()->Add(new ReturnNode(kPos, new ConstructorCallNode( + kPos, no_type_arguments, constructor, no_arguments))); +} + + +CODEGEN_TEST_RAW_RUN(AllocateNewObjectCodegen, function) { + GrowableArray arguments; + Instance& result = Instance::Handle(); + result = DartEntry::InvokeStatic(function, arguments); + const Library& app_lib = Library::Handle( + Isolate::Current()->object_store()->registered_libraries()); + const Class& cls = Class::Handle( + app_lib.LookupClass(String::Handle(String::NewSymbol("A")))); + EXPECT_EQ(cls.raw(), result.clazz()); +} + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/code_generator_x64.h b/runtime/vm/code_generator_x64.h new file mode 100644 index 00000000000..5c1877aebaa --- /dev/null +++ b/runtime/vm/code_generator_x64.h @@ -0,0 +1,50 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_CODE_GENERATOR_X64_H_ +#define VM_CODE_GENERATOR_X64_H_ + +#ifndef VM_CODE_GENERATOR_H_ +#error Do not include code_generator_x64.h directly; use assembler.h instead. +#endif + +#include "vm/allocation.h" +#include "vm/ast.h" +#include "vm/growable_array.h" + +namespace dart { + +// Forward declarations. +class Assembler; +class AstNode; +class ParsedFunction; + +class CodeGenerator : public AstNodeVisitor { + public: + CodeGenerator(Assembler* assembler, const ParsedFunction& parsed_function) { } + virtual ~CodeGenerator() { } + + bool GenerateCode() { + return false; + } + + // Add an exception handler table to code. + void FinalizeExceptionHandlers(const Code& code) { UNIMPLEMENTED(); } + + // Add Pcdescriptors to code. + void FinalizePcDescriptors(const Code& code) { UNIMPLEMENTED(); } + + // Allocate and return an arguments descriptor for the given 'num_arguments' + // and 'arguments'. If 'arguments' is NULL, treat all 'num_arguments' as + // positional arguments. + static const Array& ArgumentsDescriptor(int num_arguments, + ArgumentListNode* arguments); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(CodeGenerator); +}; + +} // namespace dart + +#endif // VM_CODE_GENERATOR_X64_H_ diff --git a/runtime/vm/code_index_table.cc b/runtime/vm/code_index_table.cc new file mode 100644 index 00000000000..6610e822e24 --- /dev/null +++ b/runtime/vm/code_index_table.cc @@ -0,0 +1,267 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/code_index_table.h" + +#include "vm/isolate.h" +#include "vm/object.h" +#include "vm/pages.h" +#include "vm/raw_object.h" +#include "vm/visitor.h" + +namespace dart { + +CodeIndexTable::CodeIndexTable() : code_pages_(NULL), + code_lists_(Array::null()), + largecode_pc_ranges_(NULL), + largecode_list_(Array::null()) { + code_pages_ = new IndexArray(kInitialSize); + ASSERT(code_pages_ != NULL); + code_lists_ = Array::New(kInitialSize); +} + + +CodeIndexTable::~CodeIndexTable() { + for (intptr_t i = 0; i < code_pages_->length(); i++) { + IndexArray* pc_ranges = code_pages_->At(i).pc_ranges; + delete pc_ranges; + } + delete code_pages_; + code_lists_ = Array::null(); + delete largecode_pc_ranges_; + largecode_list_ = Array::null(); +} + + +void CodeIndexTable::AddFunction(const Function& func) { + const Code& code = Code::Handle(func.code()); + ASSERT(!code.IsNull()); + uword entrypoint = code.EntryPoint(); // Entry point for a function. + intptr_t instr_size = code.Size(); // Instructions size for the function. + if (PageSpace::IsPageAllocatableSize(instr_size)) { + uword page_start = (entrypoint & ~(PageSpace::kPageSize - 1)); + int page_index = FindPageIndex(page_start); + if (page_index == -1) { + // We do not have an entry for this code page, add one. + page_index = AddPageIndex(page_start); + } + ASSERT(page_index != -1); + // Add the entrypoint, size and function object at the specified index. + AddFunctionToList(page_index, entrypoint, instr_size, func); + } else { + AddLargeFunction(entrypoint, instr_size, func); + } +} + + +RawFunction* CodeIndexTable::LookupFunction(uword pc) const { + const Code& code = Code::Handle(LookupCode(pc)); + if (code.IsNull()) { + return Function::null(); + } + return code.function(); +} + + +RawCode* CodeIndexTable::LookupCode(uword pc) const { + uword page_start = (pc & ~(PageSpace::kPageSize - 1)); + int page_index = FindPageIndex(page_start); + if (page_index == -1) { + // Check if the pc exists in the large pc ranges as this might be + // the pc of a large code object. This would return the large code + // or a null object if it doesn't exist in that list too. + return LookupLargeCode(pc); + } + IndexArray* pc_ranges = code_pages_->At(page_index).pc_ranges; + const Array& codes_list = Array::Handle(code_lists_); + ASSERT(!codes_list.IsNull()); + ASSERT(page_index < (codes_list.Length() - 1)); + Array& codes = Array::Handle(); + codes ^= codes_list.At(page_index); + return LookupCodeFromList(pc_ranges, codes, pc, kIsSorted); +} + + +void CodeIndexTable::VisitObjectPointers(ObjectPointerVisitor* visitor) { + ASSERT(visitor != NULL); + visitor->VisitPointer(reinterpret_cast(&code_lists_)); + visitor->VisitPointer(reinterpret_cast(&largecode_list_)); +} + + +void CodeIndexTable::Init(Isolate* isolate) { + ASSERT(isolate->code_index_table() == NULL); + CodeIndexTable* code_index_table = new CodeIndexTable(); + isolate->set_code_index_table(code_index_table); +} + + +int CodeIndexTable::AddPageIndex(uword page_start) { + ASSERT(FindPageIndex(page_start) == -1); + int page_index = code_pages_->length(); + CodePageInfo code; + code.page_start = page_start; + code.pc_ranges = new IndexArray(kInitialSize); + ASSERT(code.pc_ranges != NULL); + code_pages_->Add(code); // code gets added at 'index'. + const Array& codes_list = Array::Handle(code_lists_); + ASSERT(!codes_list.IsNull()); + const Array& codes = Array::Handle(Array::New(kInitialSize)); + codes_list.SetAt(page_index, codes); + if (code_pages_->IsFull()) { + // Grow the index table. + int new_size = code_pages_->length() + kInitialSize; + GrowCodeIndexTable(new_size); + } + return page_index; +} + + +int CodeIndexTable::FindPageIndex(uword page_start) const { + // We don't expect too many code pages (maybe max of 16) so it is + // ok to scan linearly in order to find the page_start in this index + // table. + for (int i = 0; i < code_pages_->length(); i++) { + if (code_pages_->At(i).page_start == page_start) { + return i; + } + } + return -1; +} + + +void CodeIndexTable::AddFunctionToList(int page_index, + uword entrypoint, + intptr_t size, + const Function& func) { + // Get PC ranges index array at specified index. + IndexArray* pc_ranges = code_pages_->At(page_index).pc_ranges; + ASSERT(pc_ranges != NULL); + const Array& codes_list = Array::Handle(code_lists_); + ASSERT(!codes_list.IsNull()); + // Get functions array present at specified index. + Array& codes = Array::Handle(); + codes ^= codes_list.At(page_index); + ASSERT(!codes.IsNull()); + // Asserting with an unsorted search, to ensure addition of pc was done right. + ASSERT(FindPcIndex(*pc_ranges, entrypoint, kIsNotSorted) == -1); + AddFuncHelper(pc_ranges, codes, entrypoint, size, func); + if (pc_ranges->IsFull()) { + // Grow the pc ranges table and the associated functions table. + int new_size = pc_ranges->length() + kInitialSize; + pc_ranges->Resize(new_size); + codes = Array::Grow(codes, new_size); + codes_list.SetAt(page_index, codes); + } +} + + +void CodeIndexTable::AddLargeFunction(uword entrypoint, + intptr_t size, + const Function& func) { + if (largecode_pc_ranges_ == NULL) { + // No large functions seen so far. + largecode_pc_ranges_ = new IndexArray(kInitialSize); + ASSERT(largecode_pc_ranges_ != NULL); + largecode_list_ = Array::New(kInitialSize); + } + ASSERT(FindPcIndex(*largecode_pc_ranges_, entrypoint, kIsNotSorted) == -1); + const Array& largecode_list = Array::Handle(largecode_list_); + ASSERT(!largecode_list.IsNull()); + AddFuncHelper(largecode_pc_ranges_, largecode_list, entrypoint, size, func); + if (largecode_pc_ranges_->IsFull()) { + // Grow largecode_pc_ranges_ and largecode_list_. + int new_size = largecode_pc_ranges_->length() + kInitialSize; + largecode_pc_ranges_->Resize(new_size); + largecode_list_ = Array::Grow(largecode_list, new_size); + } +} + + +void CodeIndexTable::AddFuncHelper(IndexArray* pc_ranges, + const Array& codes, + uword entrypoint, + intptr_t size, + const Function& func) { + PcRange pc_range; + pc_range.entrypoint = entrypoint; + pc_range.size = size; + intptr_t next_slot = pc_ranges->length(); + pc_ranges->Add(pc_range); // pc_range gets added at 'next_slot'. + codes.SetAt(next_slot, Code::Handle(func.code())); +} + + +RawCode* CodeIndexTable::LookupLargeCode(uword pc) const { + const Array& large_codes = Array::Handle(largecode_list_); + return LookupCodeFromList(largecode_pc_ranges_, + large_codes, + pc, + kIsNotSorted); +} + + +RawCode* CodeIndexTable::LookupCodeFromList( + IndexArray* pc_ranges, + const Array& codes, + uword pc, + bool sorted) { + if (pc_ranges == NULL) { + return Code::null(); // no entries in array so return null object. + } + intptr_t i = FindPcIndex(*pc_ranges, pc, sorted); + if (i == -1) { + return Code::null(); // no entry for pc, return null object. + } + // 'i' is in the index which holds the entry for the function, + // access the functions array at 'i' and return the function object. + ASSERT(!codes.IsNull()); + ASSERT(i < (codes.Length() - 1)); + Code& code = Code::Handle(); + code ^= codes.At(i); + return code.raw(); +} + + +intptr_t CodeIndexTable::FindPcIndex(const IndexArray& pc_ranges, + uword pc, + bool sorted) { + if (sorted) { + // The pc range entries are sorted, do a binary search to see if pc exists. + intptr_t low = 0; + intptr_t high = pc_ranges.length(); + while (low < high) { + intptr_t mid = low + (high - low) / 2; + uword entrypoint = pc_ranges.At(mid).entrypoint; + intptr_t size = pc_ranges.At(mid).size; + if (entrypoint <= pc) { + if (pc < (entrypoint + size)) { + return mid; // Found entry, return index. + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + } else { + // The pc range entries are not sorted, do a linear search. + for (intptr_t i = (pc_ranges.length() - 1); i >= 0; i--) { + uword entrypoint = pc_ranges.At(i).entrypoint; + intptr_t size = pc_ranges.At(i).size; + if (entrypoint <= pc && pc < (entrypoint + size)) { + return i; // Found entry, return index. + } + } + } + return -1; // Entry not found. +} + + +void CodeIndexTable::GrowCodeIndexTable(int new_size) { + code_pages_->Resize(new_size); + code_lists_ = Array::Grow(Array::Handle(code_lists_), new_size); +} + +} // namespace dart diff --git a/runtime/vm/code_index_table.h b/runtime/vm/code_index_table.h new file mode 100644 index 00000000000..2caf8633a97 --- /dev/null +++ b/runtime/vm/code_index_table.h @@ -0,0 +1,170 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_CODE_INDEX_TABLE_H_ +#define VM_CODE_INDEX_TABLE_H_ + +#include "vm/assert.h" +#include "vm/globals.h" + +namespace dart { + +// Forward declarations. +class Array; +class Code; +class Function; +class Isolate; +class ObjectPointerVisitor; +class RawArray; +class RawCode; +class RawFunction; + +// This class is used to lookup a Function object given a pc. +// This functionality is used while stack walking in order to find the Dart +// function corresponding to a frame (enables the pc descriptors for +// a stack frame to be located). +// Most functions fit within a normal page (PageSpace::KPageSize) but some +// functions may have code which is larger than the size of a normal page. +// These functions are referred to as large functions in this code and are +// handled by maintaining separate index lists. +class CodeIndexTable { + public: + ~CodeIndexTable(); + + // Add specified compiled function to the code index table. + void AddFunction(const Function& func); + + // Lookup code index table to find the function corresponding to the + // specified 'pc'. If there is no corresponding function a null object + // is returned. + RawFunction* LookupFunction(uword pc) const; + + // Lookup code index table to find corresponding code object. + RawCode* LookupCode(uword pc) const; + + // Visit all object pointers (support for GC). + void VisitObjectPointers(ObjectPointerVisitor* visitor); + + // Initialize the code index table for specified isolate. + static void Init(Isolate* isolate); + + private: + static const int kInitialSize = 16; + static const bool kIsSorted = true; + static const bool kIsNotSorted = false; + + template + class IndexArray { + public: + explicit IndexArray(int initial_capacity) + : length_(0), + capacity_(initial_capacity), + data_(NULL) { + data_ = reinterpret_cast(malloc(capacity_ * sizeof(T))); + ASSERT(data_ != NULL); + } + ~IndexArray() { + free(data_); + data_ = NULL; + capacity_ = 0; + length_ = 0; + } + intptr_t length() const { return length_; } + T* data() const { return data_; } + bool IsFull() const { return length_ >= capacity_; } + T& At(intptr_t index) const { + ASSERT(0 <= index); + ASSERT(index < length_); + ASSERT(length_ <= capacity_); + return data_[index]; + } + void Add(const T& value) { + ASSERT(length_ < capacity_); + data_[length_] = value; + length_ += 1; + } + void Resize(int new_capacity) { + ASSERT(new_capacity > capacity_); + T* new_data = reinterpret_cast(realloc(reinterpret_cast(data_), + new_capacity * sizeof(T))); + ASSERT(new_data != NULL); + data_ = new_data; + capacity_ = new_capacity; + } + private: + intptr_t length_; + intptr_t capacity_; + T* data_; + DISALLOW_COPY_AND_ASSIGN(IndexArray); + }; + + // PC range for a function. + typedef struct { + uword entrypoint; // Entry point for the function. + intptr_t size; // Code size for the function. + } PcRange; + + // Information about function pc ranges for a code page. + typedef struct { + uword page_start; // Start address of code page. + IndexArray* pc_ranges; // Array of entry points in a code page. + } CodePageInfo; + + // Constructor. + CodeIndexTable(); + + // Add code page information to the index table. + int AddPageIndex(uword page_start); + + // Find the index corresponding to the code page in the index table. + int FindPageIndex(uword page_start) const; + + // Add information about a function (entrypoint, size, function object) + // at the specified index of the index table. + void AddFunctionToList(int page_index, + uword entrypoint, + intptr_t size, + const Function& func); + + // Add information about a large function (entrypoint, size, function object) + // to the large function list. + void AddLargeFunction(uword entrypoint, intptr_t size, const Function& func); + + // Helper function to add a function to the list. + void AddFuncHelper(IndexArray* pc_ranges, + const Array& functions, + uword entrypoint, + intptr_t size, + const Function& func); + + // Lookup code corresponding to the pc in the large functions list + RawCode* LookupLargeCode(uword pc) const; + + // Lookup code corresponding to the pc in the functions list + // present at the specified page index. + static RawCode* LookupCodeFromList(IndexArray* pc_ranges, + const Array& functions, + uword pc, + bool sorted); + + // Find index of pc in the pc ranges array, returns -1 if the pc + // is not found in the array. + static intptr_t FindPcIndex(const IndexArray& pc_ranges, + uword pc, + bool sorted); + + // Grow the index table to the specified new size. + void GrowCodeIndexTable(int new_size); + + IndexArray* code_pages_; // Array of code pages information. + RawArray* code_lists_; // Array of pointers to code lists (arrays). + IndexArray* largecode_pc_ranges_; // pc ranges of large codes. + RawArray* largecode_list_; // Array of pointer to large code objects. + + DISALLOW_COPY_AND_ASSIGN(CodeIndexTable); +}; + +} // namespace dart + +#endif // VM_CODE_INDEX_TABLE_H_ diff --git a/runtime/vm/code_index_table_test.cc b/runtime/vm/code_index_table_test.cc new file mode 100644 index 00000000000..7842ee87506 --- /dev/null +++ b/runtime/vm/code_index_table_test.cc @@ -0,0 +1,157 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/code_index_table.h" + +#include "vm/assert.h" +#include "vm/class_finalizer.h" +#include "vm/compiler.h" +#include "vm/object.h" +#include "vm/unit_test.h" + +namespace dart { + +#if defined(TARGET_ARCH_IA32) // Compiler only implemented on IA32 now. + +TEST_CASE(CodeIndexTable) { + const int kScriptSize = 512 * KB; + const int kNumFunctions = 1024; + char scriptChars[kScriptSize]; + String& url = String::Handle(String::New("dart-test:CodeIndexTable")); + String& source = String::Handle(); + Script& script = Script::Handle(); + Library& lib = Library::Handle(); + Class& clsA = Class::Handle(); + Class& clsB = Class::Handle(); + String& function_name = String::Handle(); + Function& function = Function::Handle(); + char buffer[256]; + + // Get access to the code index table. + ASSERT(Isolate::Current() != NULL); + CodeIndexTable* code_index_table = Isolate::Current()->code_index_table(); + ASSERT(code_index_table != NULL); + + lib = Library::CoreLibrary(); + + // Load up class A with 1024 functions. + int written = OS::SNPrint(scriptChars, kScriptSize, "class A {"); + for (int i = 0; i < kNumFunctions; i++) { + OS::SNPrint(buffer, + 256, + "static foo%d(int i=1,int j=2,int k=3){return i+j+k;}", i); + written += OS::SNPrint((scriptChars + written), + (kScriptSize - written), + "%s", + buffer); + } + OS::SNPrint((scriptChars + written), (kScriptSize - written), "}"); + source = String::New(scriptChars); + script = Script::New(url, source, RawScript::kSource); + EXPECT(CompilerTest::TestCompileScript(lib, script)); + clsA = lib.LookupClass(String::Handle(String::NewSymbol("A"))); + EXPECT(!clsA.IsNull()); + ClassFinalizer::FinalizePendingClasses(); + for (int i = 0; i < kNumFunctions; i++) { + OS::SNPrint(buffer, 256, "foo%d", i); + function_name = String::New(buffer); + function = clsA.LookupStaticFunction(function_name); + EXPECT(!function.IsNull()); + EXPECT(CompilerTest::TestCompileFunction(function)); + EXPECT(function.HasCode()); + } + + // Now load up class B with 1024 functions. + written = OS::SNPrint(scriptChars, kScriptSize, "class B {"); + // Create one large function. + OS::SNPrint(buffer, sizeof(buffer), "static moo0(int i=1) { return "); + written += OS::SNPrint((scriptChars + written), + (kScriptSize - written), + "%s", + buffer); + // Currently this causes about 750KB of code to be allocated. The + // nesting level of binary operations is reduced from 50000 so this + // test will pass on Windows. Larger nesting leads to stack overflow + // in debug mode in the code generation visitor even when the stack + // reserved size is set to 2MB. + for (int i = 0; i < 35000; i++) { + OS::SNPrint(buffer, sizeof(buffer), "i+"); + written += OS::SNPrint((scriptChars + written), + (kScriptSize - written), + "%s", + buffer); + } + OS::SNPrint(buffer, sizeof(buffer), "i; }"); + written += OS::SNPrint((scriptChars + written), + (kScriptSize - written), + "%s", + buffer); + for (int i = 1; i < kNumFunctions; i++) { + OS::SNPrint(buffer, + 256, + "static moo%d(int i=1,int j=2,int k=3){return i+j+k;}", i); + written += OS::SNPrint((scriptChars + written), + (kScriptSize - written), + "%s", + buffer); + } + OS::SNPrint((scriptChars + written), (kScriptSize - written), "}"); + url = String::New("dart-test:CodeIndexTable"); + source = String::New(scriptChars); + script = Script::New(url, source, RawScript::kSource); + EXPECT(CompilerTest::TestCompileScript(lib, script)); + clsB = lib.LookupClass(String::Handle(String::NewSymbol("B"))); + EXPECT(!clsB.IsNull()); + ClassFinalizer::FinalizePendingClasses(); + for (int i = 0; i < kNumFunctions; i++) { + OS::SNPrint(buffer, 256, "moo%d", i); + function_name = String::New(buffer); + function = clsB.LookupStaticFunction(function_name); + EXPECT(!function.IsNull()); + EXPECT(CompilerTest::TestCompileFunction(function)); + EXPECT(function.HasCode()); + } + + // Now try and access these functions using the code index table. + Code& code = Code::Handle(); + uword pc; + OS::SNPrint(buffer, 256, "foo%d", 123); + function_name = String::New(buffer); + function = clsA.LookupStaticFunction(function_name); + EXPECT(!function.IsNull()); + code = function.code(); + EXPECT(code.Size() > 16); + pc = code.EntryPoint() + 16; + EXPECT(code_index_table->LookupFunction(pc) == function.raw()); + EXPECT(code_index_table->LookupCode(pc) == code.raw()); + + OS::SNPrint(buffer, 256, "moo%d", 54); + function_name = String::New(buffer); + function = clsB.LookupStaticFunction(function_name); + EXPECT(!function.IsNull()); + code = function.code(); + EXPECT(code.Size() > 16); + pc = code.EntryPoint() + 16; + EXPECT(code_index_table->LookupFunction(pc) == function.raw()); + EXPECT(code_index_table->LookupCode(pc) == code.raw()); + + // Lookup the large function + OS::SNPrint(buffer, 256, "moo%d", 0); + function_name = String::New(buffer); + function = clsB.LookupStaticFunction(function_name); + EXPECT(!function.IsNull()); + code = function.code(); + EXPECT(code.Size() > 16); + pc = code.EntryPoint() + 16; + EXPECT(code_index_table->LookupFunction(pc) == function.raw()); + EXPECT(code_index_table->LookupCode(pc) == code.raw()); + EXPECT(code.Size() > 750 * KB); + pc = code.EntryPoint() + 750 * KB; + EXPECT(code_index_table->LookupFunction(pc) == function.raw()); + EXPECT(code_index_table->LookupCode(pc) == code.raw()); +} + +#endif // TARGET_ARCH_IA32 + +} // namespace dart diff --git a/runtime/vm/code_patcher.h b/runtime/vm/code_patcher.h new file mode 100644 index 00000000000..50d6b1f5378 --- /dev/null +++ b/runtime/vm/code_patcher.h @@ -0,0 +1,60 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Class for patching compiled code. + +#ifndef VM_CODE_PATCHER_H_ +#define VM_CODE_PATCHER_H_ + +#include "vm/allocation.h" + +namespace dart { + +// Forward declaration. +class Code; +class ExternalLabel; +class Function; +class String; + +class CodePatcher : public AllStatic { + public: + // Dart static calls have a distinct, machine-dependent code pattern. + + // Patch static call to the new target. + static void PatchStaticCallAt(uword addr, uword new_target_address); + + // Overwrites code at 'at_addr' with a call to 'label'. + static void InsertCall(uword at_addr, const ExternalLabel* label); + + // Overwrites code at 'at_addr' with a jump to 'label'. + static void InsertJump(uword at_addr, const ExternalLabel* label); + + // Patch entry point with a jump as specified in the code's patch region. + static void PatchEntry(const Code& code); + + // Restore entry point with original code (i.e., before patching). + static void RestoreEntry(const Code& code); + + // Returns true if the code can be patched with a jump at beginnning (checks + // that there are no conflicts with object pointers). + static bool CodeIsPatchable(const Code& code); + + // Get static call information. + static void GetStaticCallAt(uword return_address, + Function* function, + uword* target); + + // Patch instance call to the new target. + static void PatchInstanceCallAt(uword addr, uword new_target_address); + + // Get instance call information. + static void GetInstanceCallAt(uword return_address, + String* function_name, + int* num_arguments, + int* num_named_arguments, + uword* target); +}; + +} // namespace dart + +#endif // VM_CODE_PATCHER_H_ diff --git a/runtime/vm/code_patcher_arm.cc b/runtime/vm/code_patcher_arm.cc new file mode 100644 index 00000000000..4eeddb35679 --- /dev/null +++ b/runtime/vm/code_patcher_arm.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM. +#if defined(TARGET_ARCH_ARM) + +#include "vm/code_patcher.h" + +namespace dart { + +void CodePatcher::GetStaticCallAt(uword return_address, + Function* function, + uword* target) { + UNIMPLEMENTED(); +} + + +void CodePatcher::PatchStaticCallAt(uword return_address, uword new_target) { + UNIMPLEMENTED(); +} + + +void CodePatcher::GetInstanceCallAt(uword return_address, + String* function_name, + int* num_arguments, + int* num_named_arguments, + uword* target) { + UNIMPLEMENTED(); +} + + +void CodePatcher::PatchInstanceCallAt(uword return_address, uword new_target) { + UNIMPLEMENTED(); +} + + +void CodePatcher::InsertCall(uword at_addr, const ExternalLabel* label) { + UNIMPLEMENTED(); +} + + +void CodePatcher::InsertJump(uword at_addr, const ExternalLabel* label) { + UNIMPLEMENTED(); +} + + +void CodePatcher::PatchEntry(const Code& code) { + UNIMPLEMENTED(); +} + + +void CodePatcher::RestoreEntry(const Code& code) { + UNIMPLEMENTED(); +} + + +bool CodePatcher::CodeIsPatchable(const Code& code) { + UNIMPLEMENTED(); + return false; +} + +} // namespace dart + +#endif // defined TARGET_ARCH_ARM diff --git a/runtime/vm/code_patcher_ia32.cc b/runtime/vm/code_patcher_ia32.cc new file mode 100644 index 00000000000..0a05794941e --- /dev/null +++ b/runtime/vm/code_patcher_ia32.cc @@ -0,0 +1,258 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. +#if defined(TARGET_ARCH_IA32) + +#include "vm/assembler.h" +#include "vm/code_patcher.h" +#include "vm/cpu.h" +#include "vm/instructions.h" +#include "vm/object.h" +#include "vm/raw_object.h" + +namespace dart { + +// The pattern of a Dart call is: +// 1: mov ECX, immediate 1 +// 2: mov EDX, immediate 2 +// 3: call target_address +// <- return_address +class DartCallPattern : public ValueObject { + private: + static const int kNumInstructions = 3; + static const int kInstructionSize = 5; // All instructions have same length. + + public: + explicit DartCallPattern(uword return_address) + : start_(return_address - (kNumInstructions * kInstructionSize)) { + ASSERT(*reinterpret_cast(start_) == 0xB9); + ASSERT(*reinterpret_cast(start_ + 1 * kInstructionSize) == 0xBA); + ASSERT(*reinterpret_cast(start_ + 2 * kInstructionSize) == 0xE8); + ASSERT(kInstructionSize == Assembler::kCallExternalLabelSize); + } + + uword target() const { + const uword offset = *reinterpret_cast(call_address() + 1); + return return_address() + offset; + } + + void set_target(uword target) const { + uword* target_addr = reinterpret_cast(call_address() + 1); + uword offset = target - return_address(); + *target_addr = offset; + CPU::FlushICache(call_address(), kInstructionSize); + } + + uint32_t immediate_one() const { + return *reinterpret_cast(start_ + 1); + } + + uint32_t immediate_two() const { + return *reinterpret_cast(start_ + kInstructionSize + 1); + } + + int argument_count() const { + Array& args_desc = Array::Handle(); + args_desc ^= reinterpret_cast(immediate_two()); + Smi& num_args = Smi::Handle(); + num_args ^= args_desc.At(0); + return num_args.Value(); + } + + int named_argument_count() const { + Array& args_desc = Array::Handle(); + args_desc ^= reinterpret_cast(immediate_two()); + Smi& num_args = Smi::Handle(); + num_args ^= args_desc.At(0); + Smi& num_pos_args = Smi::Handle(); + num_pos_args ^= args_desc.At(1); + return num_args.Value() - num_pos_args.Value(); + } + + private: + uword return_address() const { + return start_ + kNumInstructions * kInstructionSize; + } + + uword call_address() const { + return start_ + 2 * kInstructionSize; + } + + uword start_; + DISALLOW_IMPLICIT_CONSTRUCTORS(DartCallPattern); +}; + + +// The expected pattern of a dart static call: +// mov ECX, function_object +// mov EDX, argument_descriptor_array +// call target_address +// <- return address +class StaticCall : public DartCallPattern { + public: + explicit StaticCall(uword return_address) + : DartCallPattern(return_address) {} + + RawFunction* function() const { + Function& f = Function::Handle(); + f ^= reinterpret_cast(immediate_one()); + return f.raw(); + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(StaticCall); +}; + + +// The expected pattern of a dart instance call: +// mov ECX, function_name +// mov EDX, argument_descriptor_array +// call target_address +// <- return address +class InstanceCall : public DartCallPattern { + public: + explicit InstanceCall(uword return_address) + : DartCallPattern(return_address) {} + + RawString* function_name() const { + String& str = String::Handle(); + str ^= reinterpret_cast(immediate_one()); + return str.raw(); + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(InstanceCall); +}; + + +void CodePatcher::GetStaticCallAt(uword return_address, + Function* function, + uword* target) { + ASSERT(function != NULL); + ASSERT(target != NULL); + StaticCall call(return_address); + *target = call.target(); + *function = call.function(); +} + + +void CodePatcher::PatchStaticCallAt(uword return_address, uword new_target) { + StaticCall call(return_address); + call.set_target(new_target); +} + + +static void InsertCallOrJump(uword at_addr, + const ExternalLabel* label, + uint8_t op) { + const int kInstructionSize = 5; + *reinterpret_cast(at_addr) = op; // Call. + uword* target_addr = reinterpret_cast(at_addr + 1); + uword offset = label->address() - (at_addr + kInstructionSize); + *target_addr = offset; + CPU::FlushICache(at_addr, kInstructionSize); +} + + +void CodePatcher::InsertCall(uword at_addr, const ExternalLabel* label) { + const uint8_t kCallOp = 0xE8; + InsertCallOrJump(at_addr, label, kCallOp); +} + + +void CodePatcher::InsertJump(uword at_addr, const ExternalLabel* label) { + const uint8_t kJumpOp = 0xE9; + InsertCallOrJump(at_addr, label, kJumpOp); +} + + +static void SwapCode(intptr_t num_bytes, char* a, char* b) { + for (intptr_t i = 0; i < num_bytes; i++) { + char tmp = *a; + *a = *b; + *b = tmp; + a++; + b++; + } +} + + +// The patch code buffer contains the jmp code which will be inserted at +// entry point. +void CodePatcher::PatchEntry(const Code& code) { + Jump jmp_entry(code.EntryPoint()); + ASSERT(!jmp_entry.IsValid()); + const uword patch_buffer = code.GetPatchCodePc(); + ASSERT(patch_buffer != 0); + Jump jmp_patch(patch_buffer); + ASSERT(jmp_patch.IsValid()); + const uword jump_target = jmp_patch.TargetAddress(); + SwapCode(jmp_patch.pattern_length_in_bytes(), + reinterpret_cast(code.EntryPoint()), + reinterpret_cast(patch_buffer)); + jmp_entry.SetTargetAddress(jump_target); +} + + +// The entry point is a jmp instruction, the patch code buffer contains +// original code, the entry point contains the jump instruction. +void CodePatcher::RestoreEntry(const Code& code) { + Jump jmp_entry(code.EntryPoint()); + ASSERT(jmp_entry.IsValid()); + const uword jump_target = jmp_entry.TargetAddress(); + const uword patch_buffer = code.GetPatchCodePc(); + ASSERT(patch_buffer != 0); + // 'patch_buffer' contains original entry code. + Jump jmp_patch(patch_buffer); + ASSERT(!jmp_patch.IsValid()); + SwapCode(jmp_patch.pattern_length_in_bytes(), + reinterpret_cast(code.EntryPoint()), + reinterpret_cast(patch_buffer)); + ASSERT(jmp_patch.IsValid()); + jmp_patch.SetTargetAddress(jump_target); +} + + +bool CodePatcher::CodeIsPatchable(const Code& code) { + Jump jmp_entry(code.EntryPoint()); + if (code.Size() < (jmp_entry.pattern_length_in_bytes() * 2)) { + return false; + } + uword limit = code.EntryPoint() + jmp_entry.pattern_length_in_bytes(); + for (intptr_t i = 0; i < code.pointer_offsets_length(); i++) { + const uword addr = code.GetPointerOffsetAt(i) + code.EntryPoint(); + if (addr < limit) { + return false; + } + } + return true; +} + + +void CodePatcher::GetInstanceCallAt(uword return_address, + String* function_name, + int* num_arguments, + int* num_named_arguments, + uword* target) { + ASSERT(function_name != NULL); + ASSERT(num_arguments != NULL); + ASSERT(num_named_arguments != NULL); + ASSERT(target != NULL); + InstanceCall call(return_address); + *num_arguments = call.argument_count(); + *num_named_arguments = call.named_argument_count(); + *target = call.target(); + *function_name = call.function_name(); +} + + +void CodePatcher::PatchInstanceCallAt(uword return_address, uword new_target) { + InstanceCall call(return_address); + call.set_target(new_target); +} + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/code_patcher_ia32_test.cc b/runtime/vm/code_patcher_ia32_test.cc new file mode 100644 index 00000000000..52db759a584 --- /dev/null +++ b/runtime/vm/code_patcher_ia32_test.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" +#if defined(TARGET_ARCH_IA32) + +#include "vm/assembler.h" +#include "vm/code_patcher.h" +#include "vm/dart_entry.h" +#include "vm/instructions.h" +#include "vm/native_entry.h" +#include "vm/native_entry_test.h" +#include "vm/stub_code.h" +#include "vm/unit_test.h" + +namespace dart { + +static const intptr_t kPos = 1; // Dummy token index in non-existing source. + +CODEGEN_TEST_GENERATE(NativePatchStaticCall, test) { + SequenceNode* node_seq = test->node_sequence(); + const int num_params = 0; + const bool has_opt_params = false; + const String& native_name = + String::ZoneHandle(String::NewSymbol("TestStaticCallPatching")); + NativeFunction native_function = reinterpret_cast( + NATIVE_ENTRY_FUNCTION(TestStaticCallPatching)); + node_seq->Add(new ReturnNode(kPos, + new NativeBodyNode(kPos, + native_name, + native_function, + num_params, + has_opt_params))); +} + +CODEGEN_TEST2_GENERATE(PatchStaticCall, function, test) { + SequenceNode* node_seq = test->node_sequence(); + ArgumentListNode* arguments = new ArgumentListNode(kPos); + node_seq->Add(new ReturnNode(kPos, + new StaticCallNode(kPos, function, arguments))); +} + +CODEGEN_TEST2_RUN(PatchStaticCall, NativePatchStaticCall, Instance::null()); + +#define __ assembler-> + +ASSEMBLER_TEST_GENERATE(InsertCall, assembler) { + __ nop(); + __ nop(); + __ nop(); + __ nop(); + __ nop(); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(InsertCall, entry) { + CodePatcher::InsertCall(entry, &StubCode::MegamorphicLookupLabel()); + Call call(entry); + EXPECT_EQ(StubCode::MegamorphicLookupLabel().address(), call.TargetAddress()); +} + + +ASSEMBLER_TEST_GENERATE(InsertJump, assembler) { + __ nop(); + __ nop(); + __ nop(); + __ nop(); + __ nop(); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(InsertJump, entry) { + CodePatcher::InsertJump(entry, &StubCode::MegamorphicLookupLabel()); + Jump jump(entry); + EXPECT_EQ(StubCode::MegamorphicLookupLabel().address(), jump.TargetAddress()); +} + + +} // namespace dart + +#endif // TARGET_ARCH_IA32 diff --git a/runtime/vm/code_patcher_x64.cc b/runtime/vm/code_patcher_x64.cc new file mode 100644 index 00000000000..7007c0f7163 --- /dev/null +++ b/runtime/vm/code_patcher_x64.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_X64. +#if defined(TARGET_ARCH_X64) + +#include "vm/code_patcher.h" + +namespace dart { + +void CodePatcher::GetStaticCallAt(uword return_address, + Function* function, + uword* target) { + UNIMPLEMENTED(); +} + + +void CodePatcher::PatchStaticCallAt(uword return_address, uword new_target) { + UNIMPLEMENTED(); +} + + +void CodePatcher::GetInstanceCallAt(uword return_address, + String* function_name, + int* num_arguments, + int* num_named_arguments, + uword* target) { + UNIMPLEMENTED(); +} + + +void CodePatcher::PatchInstanceCallAt(uword return_address, uword new_target) { + UNIMPLEMENTED(); +} + + +void CodePatcher::InsertCall(uword at_addr, const ExternalLabel* label) { + UNIMPLEMENTED(); +} + + +void CodePatcher::InsertJump(uword at_addr, const ExternalLabel* label) { + UNIMPLEMENTED(); +} + + +void CodePatcher::PatchEntry(const Code& code) { + UNIMPLEMENTED(); +} + + +void CodePatcher::RestoreEntry(const Code& code) { + UNIMPLEMENTED(); +} + + +bool CodePatcher::CodeIsPatchable(const Code& code) { + UNIMPLEMENTED(); + return false; +} + +} // namespace dart + +#endif // defined TARGET_ARCH_X64 diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc new file mode 100644 index 00000000000..0e414baba23 --- /dev/null +++ b/runtime/vm/compiler.cc @@ -0,0 +1,273 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/compiler.h" + +#include "vm/assembler.h" +#include "vm/ast_printer.h" +#include "vm/code_generator.h" +#include "vm/code_index_table.h" +#include "vm/code_patcher.h" +#include "vm/dart_entry.h" +#include "vm/disassembler.h" +#include "vm/flags.h" +#include "vm/ic_stubs.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/opt_code_generator.h" +#include "vm/os.h" +#include "vm/parser.h" +#include "vm/scanner.h" +#include "vm/timer.h" + +namespace dart { + +DEFINE_FLAG(bool, disassemble, false, "Disassemble dart code."); +DEFINE_FLAG(bool, trace_compiler, false, "Trace compiler operations."); +DEFINE_FLAG(int, deoptimization_counter_threshold, 2, + "How many times we allow deoptimization before we disallow" + " certain optimizations"); + + +// Compile a function. Should call only if the function has not been compiled. +// Arg0: function object. +DEFINE_RUNTIME_ENTRY(CompileFunction, 1) { + ASSERT(arguments.Count() == kCompileFunctionRuntimeEntry.argument_count()); + const Function& function = Function::CheckedHandle(arguments.At(0)); + ASSERT(!function.HasCode()); + Compiler::CompileFunction(function); +} + + +// Extracts all encountered classes of instance calls in the unoptimized code +// by analyzing the inline caches. +// Each instance call contains a token index which is matched with an AST +// node. The collected classes are assigned to the corresponding node. +static void ExtractTypeFeedback(const Code& code, + SequenceNode* sequence_node) { + ASSERT(!code.IsNull() && !code.is_optimized()); + GrowableArray node_ids; + GrowableArray*> type_arrays; + code.ExtractTypesAtIcCalls(&node_ids, &type_arrays); + GrowableArray all_nodes; + sequence_node->CollectAllNodes(&all_nodes); + for (intptr_t i = 0; i < node_ids.length(); i++) { + ZoneGrowableArray* types = type_arrays[i]; + bool found_node = false; + for (intptr_t n = 0; n < all_nodes.length(); n++) { + if (all_nodes[n]->HasId(node_ids[i])) { + ASSERT(all_nodes[n]->CollectedClassesAtId(node_ids[i]) == NULL); + all_nodes[n]->SetCollectedClassesAtId(node_ids[i], types); + found_node = true; + break; + } + } + // There must be a node with the given token index. + ASSERT(found_node); + } +} + + +void Compiler::Compile(const Library& library, const Script& script) { + if (FLAG_trace_compiler) { + HANDLESCOPE(); + const String& script_url = String::Handle(script.url()); + // TODO(iposva): Extract script kind. + OS::Print("Compiling %s '%s'\n", "", script_url.ToCString()); + } + const String& library_key = String::Handle(library.private_key()); + script.Tokenize(library_key); + Parser::ParseCompilationUnit(library, script); +} + + +static void CompileFunctionHelper(const Function& function, bool optimized) { + TIMERSCOPE(time_compilation); + ParsedFunction parsed_function(function); + const char* function_fullname = function.ToFullyQualifiedCString(); + if (FLAG_trace_compiler) { + OS::Print("Compiling %sfunction: '%s' @ token %d\n", + (optimized ? "optimized " : ""), + function_fullname, + function.token_index()); + } + Parser::ParseFunction(&parsed_function); + CodeIndexTable* code_index_table = Isolate::Current()->code_index_table(); + ASSERT(code_index_table != NULL); + Assembler assembler; + if (optimized) { + // Transition to optimized code only from unoptimized code ... for now. + ASSERT(function.HasCode()); + ASSERT(!Code::Handle(function.code()).is_optimized()); + // Do not use type feedback to optimize a function that was deoptimized. + if (parsed_function.function().deoptimization_counter() < + FLAG_deoptimization_counter_threshold) { + ExtractTypeFeedback(Code::Handle(parsed_function.function().code()), + parsed_function.node_sequence()); + } + OptimizingCodeGenerator code_gen(&assembler, parsed_function); + code_gen.GenerateCode(); + Code& code = Code::Handle( + Code::FinalizeCode(function_fullname, &assembler)); + code.set_is_optimized(true); + code_gen.FinalizePcDescriptors(code); + code_gen.FinalizeExceptionHandlers(code); + function.SetCode(code); + code_index_table->AddFunction(function); + CodePatcher::PatchEntry(Code::Handle(function.unoptimized_code())); + if (FLAG_trace_compiler) { + OS::Print("--> patching entry 0x%x\n", + Code::Handle(function.unoptimized_code()).EntryPoint()); + } + } else { + // Unoptimized code. + if (Code::Handle(function.unoptimized_code()).IsNull()) { + ASSERT(Code::Handle(function.code()).IsNull()); + // Compiling first time. + CodeGenerator code_gen(&assembler, parsed_function); + code_gen.GenerateCode(); + const Code& code = + Code::Handle(Code::FinalizeCode(function_fullname, &assembler)); + code.set_is_optimized(false); + code_gen.FinalizePcDescriptors(code); + code_gen.FinalizeExceptionHandlers(code); + function.set_unoptimized_code(code); + function.SetCode(code); + ASSERT(CodePatcher::CodeIsPatchable(code)); + code_index_table->AddFunction(function); + } else { + // Disable optimized code. + const Code& optimized_code = Code::Handle(function.code()); + ASSERT(optimized_code.is_optimized()); + CodePatcher::PatchEntry(Code::Handle(function.code())); + if (FLAG_trace_compiler) { + OS::Print("--> patching entry 0x%x\n", + Code::Handle(function.unoptimized_code()).EntryPoint()); + } + // Use previously compiled code. + function.SetCode(Code::Handle(function.unoptimized_code())); + CodePatcher::RestoreEntry(Code::Handle(function.unoptimized_code())); + if (FLAG_trace_compiler) { + OS::Print("--> restoring entry at 0x%x\n", + Code::Handle(function.unoptimized_code()).EntryPoint()); + } + } + } + if (FLAG_trace_compiler) { + OS::Print("--> '%s' entry: 0x%x\n", + function_fullname, Code::Handle(function.code()).EntryPoint()); + } + if (FLAG_disassemble) { + OS::Print("Code for %sfunction '%s' {\n", + optimized ? "optimized " : "", function_fullname); + const Code& code = Code::Handle(function.code()); + const Instructions& instructions = + Instructions::Handle(code.instructions()); + uword start = instructions.EntryPoint(); + Disassembler::Disassemble(start, start + assembler.CodeSize()); + OS::Print("}\n"); + OS::Print("Pointer offsets for function: {\n"); + for (intptr_t i = 0; i < code.pointer_offsets_length(); i++) { + const uword addr = code.GetPointerOffsetAt(i) + code.EntryPoint(); + Object& obj = Object::Handle(); + obj = *reinterpret_cast(addr); + OS::Print(" %d : 0x%x '%s'\n", + code.GetPointerOffsetAt(i), addr, obj.ToCString()); + } + OS::Print("}\n"); + OS::Print("PC Descriptors for function '%s' {\n", function_fullname); + OS::Print("(pc, kind, id, try-index, token-index)\n"); + const PcDescriptors& descriptors = + PcDescriptors::Handle(code.pc_descriptors()); + OS::Print("%s", descriptors.ToCString()); + OS::Print("}\n"); + OS::Print("Exception Handlers for function '%s' {\n", function_fullname); + const ExceptionHandlers& handlers = + ExceptionHandlers::Handle(code.exception_handlers()); + OS::Print("%s", handlers.ToCString()); + OS::Print("}\n"); + } +} + + +void Compiler::CompileFunction(const Function& function) { + CompileFunctionHelper(function, false); +} + + +void Compiler::CompileOptimizedFunction(const Function& function) { + CompileFunctionHelper(function, true); +} + + +void Compiler::CompileAllFunctions(const Class& cls) { + Array& functions = Array::Handle(cls.functions()); + Function& func = Function::Handle(); + for (int i = 0; i < functions.Length(); i++) { + func ^= functions.At(i); + ASSERT(!func.IsNull()); + if (!func.HasCode() && !func.IsAbstract()) { + CompileFunction(func); + } + } +} + + +RawInstance* Compiler::ExecuteOnce(SequenceNode* fragment) { + if (FLAG_trace_compiler) { + OS::Print("compiling expression: "); + AstPrinter::PrintNode(fragment); + } + + // Create a dummy function object for the code generator. + const char* kEvalConst = "eval_const"; + const Function& func = Function::Handle(Function::New( + String::Handle(String::NewSymbol(kEvalConst)), + RawFunction::kConstImplicitGetter, + true, // static function. + false, // not const function. + fragment->token_index())); + + func.set_result_type(Type::Handle(Type::VarType())); + func.set_num_fixed_parameters(0); + func.set_num_optional_parameters(0); + + // The function needs to be associated with a named Class: the interface + // Function fits the bill. + func.set_owner(Class::Handle( + Type::Handle(Type::FunctionInterface()).type_class())); + + // We compile the function here, even though InvokeStatic() below + // would compile func automatically. We are checking fewer invariants + // here. + ParsedFunction parsed_function(func); + parsed_function.set_node_sequence(fragment); + parsed_function.set_default_parameter_values(Array::Handle()); + + Assembler assembler; + CodeGenerator code_gen(&assembler, parsed_function); + code_gen.GenerateCode(); + const Code& code = Code::Handle(Code::FinalizeCode(kEvalConst, &assembler)); + + func.SetCode(code); + CodeIndexTable* code_index_table = Isolate::Current()->code_index_table(); + ASSERT(code_index_table != NULL); + code_index_table->AddFunction(func); + // TODO(hausner): We need a way to remove these one-time execution + // functions from the global code description (PC mapping) tables so + // we don't pollute the system unnecessarily with stale data. + code_gen.FinalizePcDescriptors(code); + code_gen.FinalizeExceptionHandlers(code); + + GrowableArray arguments; // no arguments. + Instance& result = Instance::Handle(DartEntry::InvokeStatic(func, arguments)); + if (result.IsUnhandledException()) { + // TODO(srdjan): implement proper exit from compiler. + UNIMPLEMENTED(); + } + return result.raw(); +} + + +} // namespace dart diff --git a/runtime/vm/compiler.h b/runtime/vm/compiler.h new file mode 100644 index 00000000000..7bebed24a00 --- /dev/null +++ b/runtime/vm/compiler.h @@ -0,0 +1,47 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_COMPILER_H_ +#define VM_COMPILER_H_ + +#include "vm/allocation.h" +#include "vm/growable_array.h" +#include "vm/runtime_entry.h" + +namespace dart { + +// Forward declarations. +class Class; +class Function; +class Library; +class RawInstance; +class Script; +class SequenceNode; + +DECLARE_RUNTIME_ENTRY(CompileFunction); + +class Compiler : public AllStatic { + public: + // Extracts class, interface, function symbols from the script and populates + // the symbol tables and the class dictionary of the library. + static void Compile(const Library& library, const Script& script); + + // Generates code for given function and sets its code field. + static void CompileFunction(const Function& function); + + // Generates optimized code for function. + static void CompileOptimizedFunction(const Function& function); + + // Generates and executes code for a given code fragment, e.g. a + // compile time constant expression. Returns the result returned + // by the fragment. + static RawInstance* ExecuteOnce(SequenceNode* fragment); + + // Eagerly compiles all functions in a class. + static void CompileAllFunctions(const Class& cls); +}; + +} // namespace dart + +#endif // VM_COMPILER_H_ diff --git a/runtime/vm/compiler_stats.cc b/runtime/vm/compiler_stats.cc new file mode 100644 index 00000000000..04d3c9514fa --- /dev/null +++ b/runtime/vm/compiler_stats.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/compiler_stats.h" + +#include "vm/flags.h" +#include "vm/timer.h" + + +namespace dart { + +DEFINE_FLAG(bool, compiler_stats, false, "Compiler stat counters."); + +// Bytes allocated for generated code. +intptr_t CompilerStats::code_allocated = 0; + +// Total number of characters in source. +intptr_t CompilerStats::src_length = 0; + +// Cumulative runtime of parser. +Timer CompilerStats::parser_timer(true, "parser timer"); + +// Cumulative runtime of scanner. +Timer CompilerStats::scanner_timer(true, "scanner timer"); + +intptr_t CompilerStats::num_tokens_total = 0; +intptr_t CompilerStats::num_tokens_consumed = 0; +intptr_t CompilerStats::num_token_checks = 0; +intptr_t CompilerStats::num_tokens_rewind = 0; +intptr_t CompilerStats::num_tokens_lookahead = 0; + +void CompilerStats::Print() { + if (!FLAG_compiler_stats) { + return; + } + OS::Print("==== Compiler Stats ====\n"); + OS::Print("Number of tokens: %ld\n", num_tokens_total); + OS::Print("Tokens consumed: %ld (%.2f times number of tokens)\n", + num_tokens_consumed, + (1.0 * num_tokens_consumed) / num_tokens_total); + OS::Print("Tokens checked: %ld (%.2f times tokens consumed)\n", + num_token_checks, (1.0 * num_token_checks) / num_tokens_consumed); + OS::Print("Token rewind: %ld (%ld%% of tokens checked)\n", + num_tokens_rewind, (100 * num_tokens_rewind) / num_token_checks); + OS::Print("Token lookahead: %ld (%ld%% of tokens checked)\n", + num_tokens_lookahead, + (100 * num_tokens_lookahead) / num_token_checks); + OS::Print("Source length: %ld characters\n", src_length); + intptr_t scan_usecs = scanner_timer.TotalElapsedTime(); + OS::Print("Scanner time: %ld msecs\n", + scan_usecs / 1000); + intptr_t parse_usecs = parser_timer.TotalElapsedTime(); + OS::Print("Compilation time: %ld msecs\n", + parse_usecs / 1000); + OS::Print("Compilation speed: %ld tokens per msec\n", + 1000 * num_tokens_total / parse_usecs); + OS::Print("Code size: %ld KB\n", + code_allocated / 1024); + OS::Print("Code density: %ld tokens per KB\n", + num_tokens_total * 1024 / code_allocated); +} + +} // namespace dart + diff --git a/runtime/vm/compiler_stats.h b/runtime/vm/compiler_stats.h new file mode 100644 index 00000000000..462542e9b20 --- /dev/null +++ b/runtime/vm/compiler_stats.h @@ -0,0 +1,38 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_COMPILER_STATS_H_ +#define VM_COMPILER_STATS_H_ + +#include "vm/allocation.h" +#include "vm/flags.h" +#include "vm/timer.h" + + + +namespace dart { + +DECLARE_FLAG(bool, compiler_stats); + + +class CompilerStats : AllStatic { + public: + static intptr_t num_tokens_total; + static intptr_t num_tokens_consumed; + static intptr_t num_token_checks; + static intptr_t num_tokens_rewind; + static intptr_t num_tokens_lookahead; + + static intptr_t src_length; // Total number of characters in source. + static intptr_t code_allocated; // Bytes allocated for generated code. + static Timer parser_timer; // Cumulative runtime of parser. + static Timer scanner_timer; // Cumulative runtime of scanner. + + static void Print(); +}; + + +} // namespace dart + +#endif // VM_COMPILER_STATS_H_ diff --git a/runtime/vm/compiler_test.cc b/runtime/vm/compiler_test.cc new file mode 100644 index 00000000000..9d7d2342cc7 --- /dev/null +++ b/runtime/vm/compiler_test.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/compiler.h" + +#include "vm/assert.h" +#include "vm/object.h" +#include "vm/unit_test.h" + +namespace dart { + +#if defined(TARGET_ARCH_IA32) // Compiler only implemented on IA32 now. + +TEST_CASE(CompileScript) { + const char* kScriptChars = + "class A {\n" + " static foo() { return 42; }\n" + "}\n"; + String& url = String::Handle(String::New("dart-test:CompileScript")); + String& source = String::Handle(String::New(kScriptChars)); + Script& script = Script::Handle(Script::New(url, source, RawScript::kSource)); + Library& lib = Library::Handle(Library::CoreLibrary()); + EXPECT(CompilerTest::TestCompileScript(lib, script)); +} + + +TEST_CASE(CompileFunction) { + const char* kScriptChars = + "class A {\n" + " static foo() { return 42; }\n" + " static moo() {\n" + " // A.foo();\n" + " }\n" + "}\n"; + String& url = String::Handle(String::New("dart-test:CompileFunction")); + String& source = String::Handle(String::New(kScriptChars)); + Script& script = Script::Handle(Script::New(url, source, RawScript::kSource)); + Library& lib = Library::Handle(Library::CoreLibrary()); + EXPECT(CompilerTest::TestCompileScript(lib, script)); + Class& cls = Class::Handle( + lib.LookupClass(String::Handle(String::NewSymbol("A")))); + EXPECT(!cls.IsNull()); + String& function_foo_name = String::Handle(String::New("foo")); + Function& function_foo = + Function::Handle(cls.LookupStaticFunction(function_foo_name)); + EXPECT(!function_foo.IsNull()); + + EXPECT(CompilerTest::TestCompileFunction(function_foo)); + EXPECT(function_foo.HasCode()); + + String& function_moo_name = String::Handle(String::New("moo")); + Function& function_moo = + Function::Handle(cls.LookupStaticFunction(function_moo_name)); + EXPECT(!function_moo.IsNull()); + + EXPECT(CompilerTest::TestCompileFunction(function_moo)); + EXPECT(function_moo.HasCode()); +} + +#endif // TARGET_ARCH_IA32 + +} // namespace dart diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h new file mode 100644 index 00000000000..9a728904cc5 --- /dev/null +++ b/runtime/vm/constants_arm.h @@ -0,0 +1,61 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_CONSTANTS_ARM_H_ +#define VM_CONSTANTS_ARM_H_ + +namespace dart { + +enum Register { + R0 = 0, + R1 = 1, + R2 = 2, + R3 = 3, + R4 = 4, + R5 = 5, + R6 = 6, + R7 = 7, + R8 = 8, + R9 = 9, + R10 = 10, + R11 = 11, + R12 = 12, + R13 = 13, + R14 = 14, + R15 = 15, + FP = 11, + IP = 12, + SP = 13, + LR = 14, + PC = 15, + kNumberOfCoreRegisters = 16, + kNoRegister = -1, +}; + + +// Values for the condition field as defined in section A3.2. +enum Condition { + kNoCondition = -1, + EQ = 0, // equal + NE = 1, // not equal + CS = 2, // carry set/unsigned higher or same + CC = 3, // carry clear/unsigned lower + MI = 4, // minus/negative + PL = 5, // plus/positive or zero + VS = 6, // overflow + VC = 7, // no overflow + HI = 8, // unsigned higher + LS = 9, // unsigned lower or same + GE = 10, // signed greater than or equal + LT = 11, // signed less than + GT = 12, // signed greater than + LE = 13, // signed less than or equal + AL = 14, // always (unconditional) + kSpecialCondition = 15, // special condition (refer to section A3.2.1) + kMaxCondition = 16, +}; + +} // namespace dart + +#endif // VM_CONSTANTS_ARM_H_ diff --git a/runtime/vm/constants_ia32.h b/runtime/vm/constants_ia32.h new file mode 100644 index 00000000000..c3cda3c5f8a --- /dev/null +++ b/runtime/vm/constants_ia32.h @@ -0,0 +1,124 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_CONSTANTS_IA32_H_ +#define VM_CONSTANTS_IA32_H_ + +#include "vm/assert.h" + +namespace dart { + +enum Register { + EAX = 0, + ECX = 1, + EDX = 2, + EBX = 3, + ESP = 4, + EBP = 5, + ESI = 6, + EDI = 7, + kNumberOfCpuRegisters = 8, + kFirstByteUnsafeRegister = 4, + kNoRegister = -1 // Signals an illegal register. +}; + + +enum ByteRegister { + AL = 0, + CL = 1, + DL = 2, + BL = 3, + AH = 4, + CH = 5, + DH = 6, + BH = 7, + kNoByteRegister = -1 // Signals an illegal register. +}; + + +enum XmmRegister { + XMM0 = 0, + XMM1 = 1, + XMM2 = 2, + XMM3 = 3, + XMM4 = 4, + XMM5 = 5, + XMM6 = 6, + XMM7 = 7, + kNumberOfXmmRegisters = 8, + kNoXmmRegister = -1 // Signals an illegal register. +}; + + +// Register aliases. +const Register CTX = ESI; // Caches current context in generated code. + +// Exception object is passed in this register to the catch handlers when an +// exception is thrown. +const Register kExceptionObjectReg = EAX; + +// Stack trace object is passed in this register to the catch handlers when +// an exception is thrown. +const Register kStackTraceObjectReg = EDX; + +enum ScaleFactor { + TIMES_1 = 0, + TIMES_2 = 1, + TIMES_4 = 2, + TIMES_8 = 3 +}; + + +enum Condition { + OVERFLOW = 0, + NO_OVERFLOW = 1, + BELOW = 2, + ABOVE_EQUAL = 3, + EQUAL = 4, + NOT_EQUAL = 5, + BELOW_EQUAL = 6, + ABOVE = 7, + SIGN = 8, + NOT_SIGN = 9, + PARITY_EVEN = 10, + PARITY_ODD = 11, + LESS = 12, + GREATER_EQUAL = 13, + LESS_EQUAL = 14, + GREATER = 15, + + ZERO = EQUAL, + NOT_ZERO = NOT_EQUAL, + NEGATIVE = SIGN, + POSITIVE = NOT_SIGN +}; + + +class Instr { + public: + static const uint8_t kHltInstruction = 0xF4; + // We prefer not to use the int3 instruction since it conflicts with gdb. + static const uint8_t kBreakPointInstruction = kHltInstruction; + static const int kBreakPointInstructionSize = 1; + + bool IsBreakPoint() { + ASSERT(kBreakPointInstructionSize == 1); + return (*reinterpret_cast(this)) == kBreakPointInstruction; + } + + // Instructions are read out of a code stream. The only way to get a + // reference to an instruction is to convert a pointer. There is no way + // to allocate or create instances of class Instr. + // Use the At(pc) function to create references to Instr. + static Instr* At(uword pc) { return reinterpret_cast(pc); } + + private: + DISALLOW_ALLOCATION(); + // We need to prevent the creation of instances of class Instr. + DISALLOW_IMPLICIT_CONSTRUCTORS(Instr); +}; + +} // namespace dart + +#endif // VM_CONSTANTS_IA32_H_ diff --git a/runtime/vm/constants_x64.h b/runtime/vm/constants_x64.h new file mode 100644 index 00000000000..0e0412b470d --- /dev/null +++ b/runtime/vm/constants_x64.h @@ -0,0 +1,33 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_CONSTANTS_X64_H_ +#define VM_CONSTANTS_X64_H_ + +namespace dart { + +enum Register { + RAX = 0, + RCX = 1, + RDX = 2, + RBX = 3, + RSP = 4, + RBP = 5, + RSI = 6, + RDI = 7, + R8 = 8, + R9 = 9, + R10 = 10, + R11 = 11, + R12 = 12, + R13 = 13, + R14 = 14, + R15 = 15, + kNumberOfCpuRegisters = 16, + kNoRegister = -1 // Signals an illegal register. +}; + +} // namespace dart + +#endif // VM_CONSTANTS_X64_H_ diff --git a/runtime/vm/corelib_impl_in.cc b/runtime/vm/corelib_impl_in.cc new file mode 100644 index 00000000000..fc95253025a --- /dev/null +++ b/runtime/vm/corelib_impl_in.cc @@ -0,0 +1,16 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bootstrap.h" + + +namespace dart { + +// The string on the next line will be filled in with the contents of the +// bootstrap dart files. +const char Bootstrap::corelib_impl_source_[] = { + %s +}; + +} // namespace dart diff --git a/runtime/vm/corelib_in.cc b/runtime/vm/corelib_in.cc new file mode 100644 index 00000000000..37c60ac96db --- /dev/null +++ b/runtime/vm/corelib_in.cc @@ -0,0 +1,15 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bootstrap.h" + +namespace dart { + +// The string on the next line will be filled in with the contents of the +// bootstrap dart files. +const char Bootstrap::corelib_source_[] = { + %s +}; + +} // namespace dart diff --git a/runtime/vm/cpu.h b/runtime/vm/cpu.h new file mode 100644 index 00000000000..4b35a413636 --- /dev/null +++ b/runtime/vm/cpu.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_CPU_H_ +#define VM_CPU_H_ + +#include "vm/allocation.h" + +namespace dart { + +// Forward Declarations. +class Instance; +class UnhandledException; + + +class CPU : public AllStatic { + public: + static void FlushICache(uword start, uword size); + static void JumpToExceptionHandler(uword pc, + uword sp, + uword fp, + const Instance& exception_object, + const Instance& stacktrace_object); + static void JumpToUnhandledExceptionHandler( + uword pc, + uword sp, + uword fp, + const UnhandledException& unhandled_exception); + static const char* Id(); +}; + +} // namespace dart + +#endif // VM_CPU_H_ diff --git a/runtime/vm/cpu_arm.cc b/runtime/vm/cpu_arm.cc new file mode 100644 index 00000000000..64087960f14 --- /dev/null +++ b/runtime/vm/cpu_arm.cc @@ -0,0 +1,58 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" + +#if defined(TARGET_ARCH_ARM) + +#if defined(HOST_ARCH_ARM) +#include /* NOLINT */ +#include /* NOLINT */ +#endif + +#include "vm/cpu.h" + +namespace dart { + +void CPU::FlushICache(uword start, uword size) { +#if defined(HOST_ARCH_ARM) + +#if defined(__ARM_EABI__) && !defined(__thumb__) + syscall(__ARM_NR_cacheflush, start, start + size, 0); +#else +#error FlushICache only tested/supported on EABI ARM currently. +#endif + +#else // defined(HOST_ARCH_ARM) + // When running in simulated mode we do not need to flush the ICache because + // we are not running on the actual hardware. +#endif // defined(HOST_ARCH_ARM) +} + + +void CPU::JumpToExceptionHandler(uword pc, + uword sp, + uword fp, + const Instance& exception_object, + const Instance& stacktrace_object) { + UNIMPLEMENTED(); +} + + +void CPU::JumpToUnhandledExceptionHandler( + uword pc, + uword sp, + uword fp, + const UnhandledException& unhandled_exception) { + UNIMPLEMENTED(); +} + + +const char* CPU::Id() { + return "arm"; +} + +} // namespace dart + +#endif // defined TARGET_ARCH_ARM diff --git a/runtime/vm/cpu_ia32.cc b/runtime/vm/cpu_ia32.cc new file mode 100644 index 00000000000..5eaeeeadf09 --- /dev/null +++ b/runtime/vm/cpu_ia32.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" + +#if defined(TARGET_ARCH_IA32) + +#include "vm/cpu.h" + +#include "vm/constants_ia32.h" +#include "vm/heap.h" +#include "vm/isolate.h" +#include "vm/object.h" + +namespace dart { + +void CPU::FlushICache(uword start, uword size) { + // Nothing to be done here. +} + + +void CPU::JumpToExceptionHandler(uword program_counter, + uword stack_pointer, + uword frame_pointer, + const Instance& exception_object, + const Instance& stacktrace_object) { + NoGCScope no_gc; + ASSERT(!exception_object.IsNull()); + RawInstance* exception = exception_object.raw(); + RawInstance* stacktrace = stacktrace_object.raw(); + + // Prepare for unwinding frames by destroying all the stack resources + // in the previous frames. + Isolate* isolate = Isolate::Current(); + while (isolate->top_resource() != NULL && + (reinterpret_cast(isolate->top_resource()) < stack_pointer)) { + isolate->top_resource()->~StackResource(); + } + + // Set up the appropriate register state and jump to the handler. + ASSERT(kExceptionObjectReg == EAX); + ASSERT(kStackTraceObjectReg == EDX); +#if defined(TARGET_OS_WINDOWS) + __asm { + mov eax, exception + mov edx, stacktrace + mov ebx, program_counter + mov ecx, frame_pointer + mov edi, stack_pointer + mov ebp, ecx + mov esp, edi + jmp ebx + } +#else + asm volatile("mov %[exception], %%eax;" + "mov %[stacktrace], %%edx;" + "mov %[pc], %%ebx;" + "mov %[fp], %%ecx;" + "mov %[sp], %%edi;" + "mov %%ecx, %%ebp;" + "mov %%edi, %%esp;" + "jmp *%%ebx;" + : + : [exception] "m" (exception), + [stacktrace] "m" (stacktrace), + [pc] "m" (program_counter), + [sp] "m" (stack_pointer), + [fp] "m" (frame_pointer)); +#endif + UNREACHABLE(); +} + + +void CPU::JumpToUnhandledExceptionHandler( + uword program_counter, + uword stack_pointer, + uword frame_pointer, + const UnhandledException& unhandled_exception_object) { + NoGCScope no_gc; + ASSERT(!unhandled_exception_object.IsNull()); + RawUnhandledException* unhandled_exception = unhandled_exception_object.raw(); + + // Prepare for unwinding frames by destroying all the stack resources + // in the previous frames. + Isolate* isolate = Isolate::Current(); + while (isolate->top_resource() != NULL && + (reinterpret_cast(isolate->top_resource()) < stack_pointer)) { + isolate->top_resource()->~StackResource(); + } + + // Set up the unhandled exception object as the return value in EAX + // and continue from the invocation stub. +#if defined(TARGET_OS_WINDOWS) + __asm { + mov eax, unhandled_exception + mov ebx, program_counter + mov ecx, frame_pointer + mov edi, stack_pointer + mov ebp, ecx + mov esp, edi + jmp ebx + } +#else + asm volatile("mov %[unhandled_exception], %%eax;" + "mov %[pc], %%ebx;" + "mov %[fp], %%ecx;" + "mov %[sp], %%edi;" + "mov %%ecx, %%ebp;" + "mov %%edi, %%esp;" + "jmp *%%ebx;" + : + : [unhandled_exception] "m" (unhandled_exception), + [pc] "m" (program_counter), + [sp] "m" (stack_pointer), + [fp] "m" (frame_pointer)); +#endif + UNREACHABLE(); +} + + +const char* CPU::Id() { + return "ia32"; +} + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/cpu_test.cc b/runtime/vm/cpu_test.cc new file mode 100644 index 00000000000..001e266d5bf --- /dev/null +++ b/runtime/vm/cpu_test.cc @@ -0,0 +1,24 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/cpu.h" +#include "vm/globals.h" +#include "vm/unit_test.h" + +namespace dart { + +UNIT_TEST_CASE(Id) { +#if defined(TARGET_ARCH_IA32) + EXPECT_STREQ("ia32", CPU::Id()); +#elif defined(TARGET_ARCH_X64) + EXPECT_STREQ("x64", CPU::Id()); +#elif defined(TARGET_ARCH_ARM) + EXPECT_STREQ("arm", CPU::Id()); +#else +#error Architecture was not detected as supported by Dart. +#endif +} + +} // namespace dart diff --git a/runtime/vm/cpu_x64.cc b/runtime/vm/cpu_x64.cc new file mode 100644 index 00000000000..f0655f5b61b --- /dev/null +++ b/runtime/vm/cpu_x64.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" + +#if defined(TARGET_ARCH_X64) + +#include "vm/cpu.h" + +namespace dart { + +void CPU::FlushICache(uword start, uword size) { + // Nothing to be done here. +} + + +void CPU::JumpToExceptionHandler(uword pc, + uword sp, + uword fp, + const Instance& exception_object, + const Instance& stacktrace_object) { + UNIMPLEMENTED(); +} + + +void CPU::JumpToUnhandledExceptionHandler( + uword pc, + uword sp, + uword fp, + const UnhandledException& unhandled_exception) { + UNIMPLEMENTED(); +} + + +const char* CPU::Id() { + return "x64"; +} + +} // namespace dart + +#endif // defined TARGET_ARCH_X64 diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc new file mode 100644 index 00000000000..949a63cc55b --- /dev/null +++ b/runtime/vm/dart.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/dart.h" + +#include "vm/code_index_table.h" +#include "vm/flags.h" +#include "vm/handles.h" +#include "vm/heap.h" +#include "vm/isolate.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/port.h" +#include "vm/snapshot.h" +#include "vm/stub_code.h" +#include "vm/virtual_memory.h" +#include "vm/zone.h" + +namespace dart { + +Isolate* Dart::vm_isolate_ = NULL; +DebugInfo* Dart::pprof_symbol_generator_ = NULL; + +bool Dart::InitOnce(int argc, char** argv, + Dart_IsolateInitCallback callback) { + // TODO(iposva): Fix race condition here. + if (vm_isolate_ != NULL) { + return false; + } + OS::InitOnce(); + Flags::ProcessCommandLineFlags(argc, argv); + VirtualMemory::InitOnce(); + Isolate::InitOnce(); + // Create the VM isolate and finish the VM initialization. + { + ASSERT(vm_isolate_ == NULL); + vm_isolate_ = Isolate::Init(); + Zone zone; + HandleScope handle_scope; + Heap::Init(vm_isolate_); + ObjectStore::Init(vm_isolate_); + Object::InitOnce(); + StubCode::InitOnce(); + PortMap::InitOnce(); + Scanner::InitOnce(); + } + Isolate::SetCurrent(NULL); // Unregister the VM isolate from this thread. + Isolate::SetInitCallback(callback); + return true; +} + + +Isolate* Dart::CreateIsolate(void* snapshot_buffer, void* data) { + // Create and initialize a new isolate. + Isolate* isolate = Isolate::Init(); + Zone zone; + HandleScope handle_scope; + Heap::Init(isolate); + ObjectStore::Init(isolate); + + if (snapshot_buffer == NULL) { + Object::Init(isolate); + } else { + // Initialize from snapshot (this should replicate the functionality + // of Object::Init(..) in a regular isolate creation path. + Object::InitFromSnapshot(isolate); + Snapshot* snapshot = Snapshot::SetupFromBuffer(snapshot_buffer); + SnapshotReader reader(snapshot, isolate->heap(), isolate->object_store()); + reader.ReadFullSnapshot(); + } + + StubCode::Init(isolate); + CodeIndexTable::Init(isolate); + + // Give the embedder a shot at setting up this isolate. + // Isolates spawned from within this isolate will be given the callback data + // returned by the callback. + data = Isolate::InitCallback()(data); + // TODO(iposva): Shutdown the isolate on failure. + isolate->set_init_callback_data(data); + return isolate; +} + + +void Dart::ShutdownIsolate() { + Isolate* isolate = Isolate::Current(); + isolate->Shutdown(); + delete isolate; +} + +} // namespace dart diff --git a/runtime/vm/dart.h b/runtime/vm/dart.h new file mode 100644 index 00000000000..f1c52a90b5d --- /dev/null +++ b/runtime/vm/dart.h @@ -0,0 +1,39 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_DART_H_ +#define VM_DART_H_ + +#include "include/dart_api.h" +#include "vm/allocation.h" + +namespace dart { + +// Forward declarations. +class DebugInfo; +class Isolate; + +class Dart : public AllStatic { + public: + static bool InitOnce(int argc, char** argv, + Dart_IsolateInitCallback callback); + + static Isolate* CreateIsolate(void* snapshot, void* data); + static void ShutdownIsolate(); + + static Isolate* vm_isolate() { return vm_isolate_; } + + static void set_pprof_symbol_generator(DebugInfo* value) { + pprof_symbol_generator_ = value; + } + static DebugInfo* pprof_symbol_generator() { return pprof_symbol_generator_; } + + private: + static Isolate* vm_isolate_; + static DebugInfo* pprof_symbol_generator_; +}; + +} // namespace dart + +#endif // VM_DART_H_ diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc new file mode 100644 index 00000000000..93d3cfa1128 --- /dev/null +++ b/runtime/vm/dart_api_impl.cc @@ -0,0 +1,1599 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "include/dart_api.h" + +#include "vm/bigint_operations.h" +#include "vm/class_finalizer.h" +#include "vm/compiler.h" +#include "vm/dart.h" +#include "vm/dart_api_impl.h" +#include "vm/dart_api_state.h" +#include "vm/dart_entry.h" +#include "vm/debuginfo.h" +#include "vm/exceptions.h" +#include "vm/growable_array.h" +#include "vm/longjump.h" +#include "vm/native_entry.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/port.h" +#include "vm/resolver.h" +#include "vm/snapshot.h" +#include "vm/stack_frame.h" +#include "vm/timer.h" +#include "vm/verifier.h" + +namespace dart { + +// TODO(iposva): This is a placeholder for the eventual external Dart API. +DART_EXPORT bool Dart_Initialize(int argc, + char** argv, + Dart_IsolateInitCallback callback) { + return Dart::InitOnce(argc, argv, callback); +} + + +DART_EXPORT Dart_Isolate Dart_CreateIsolate(void* snapshot, void* data) { + ASSERT(Isolate::Current() == NULL); + Isolate* isolate = Dart::CreateIsolate(snapshot, data); + START_TIMER(time_total_runtime); + return reinterpret_cast(isolate); +} + + +DART_EXPORT void Dart_ShutdownIsolate() { + ASSERT(Isolate::Current() != NULL); + STOP_TIMER(time_total_runtime); + Dart::ShutdownIsolate(); +} + + +DART_EXPORT Dart_Isolate Dart_CurrentIsolate() { + return reinterpret_cast(Isolate::Current()); +} + + +DART_EXPORT void Dart_EnterIsolate(Dart_Isolate dart_isolate) { + Isolate* isolate = reinterpret_cast(dart_isolate); + ASSERT(Isolate::Current() == NULL); + Isolate::SetCurrent(isolate); +} + + +DART_EXPORT void Dart_ExitIsolate() { + ASSERT(Isolate::Current() != NULL); + Isolate::SetCurrent(NULL); +} + + +DART_EXPORT void Dart_RunLoop() { + Isolate* isolate = Isolate::Current(); + // Keep listening until there are no active receive ports. + while (isolate->active_ports() > 0) { + Zone zone; + HandleScope handle_scope; + + PortMessage* message = PortMap::ReceiveMessage(0); + message->Handle(); + delete message; + } +} + + +static void SetupErrorResult(Dart_Result* result) { + // Make a copy of the error message as the original message string + // may get deallocated when we return back from the Dart API call. + const String& error = String::Handle( + Isolate::Current()->object_store()->sticky_error()); + const char* errmsg = error.ToCString(); + intptr_t errlen = strlen(errmsg) + 1; + char* msg = reinterpret_cast(Api::Allocate(errlen)); + OS::SNPrint(msg, errlen, "%s", errmsg); + result->type_ = kRetError; + result->retval_.errmsg = msg; +} + + +// NOTE: Need to pass 'result' as a parameter here in order to avoid +// warning: variable 'result' might be clobbered by 'longjmp' or 'vfork' +// which shows up because of the use of setjmp. +static void CompileSource(const Library& lib, + const String& url, + const String& source, + RawScript::Kind kind, + Dart_Result* result) { + const Script& script = Script::Handle(Script::New(url, source, kind)); + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + LongJump* base = isolate->long_jump_base(); + LongJump jump; + isolate->set_long_jump_base(&jump); + if (setjmp(*jump.Set()) == 0) { + Compiler::Compile(lib, script); + result->type_ = kRetObject; + result->retval_.obj_value = Api::NewLocalHandle(lib); + } else { + SetupErrorResult(result); + } + isolate->set_long_jump_base(base); +} + + +DART_EXPORT Dart_Result Dart_LoadScript(Dart_Handle url, + Dart_Handle source, + Dart_LibraryTagHandler handler) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + TIMERSCOPE(time_script_loading); + const String& url_str = String::CheckedHandle(Api::UnwrapHandle(url)); + const String& source_str = String::CheckedHandle(Api::UnwrapHandle(source)); + Library& library = Library::Handle(isolate->object_store()->root_library()); + if (!library.IsNull()) { + RETURN_FAILURE("Script already loaded"); + } + isolate->set_library_tag_handler(handler); + library = Library::New(url_str); + library.Register(); + Dart_Result result; + CompileSource(library, url_str, source_str, RawScript::kScript, &result); + return result; +} + + +DEFINE_FLAG(bool, compile_all, false, "Eagerly compile all code."); + +static void CompileAll(Dart_Result* result) { + result->type_ = kRetCBool; + result->retval_.bool_value = true; + if (FLAG_compile_all) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + LongJump* base = isolate->long_jump_base(); + LongJump jump; + isolate->set_long_jump_base(&jump); + if (setjmp(*jump.Set()) == 0) { + Library::CompileAll(); + } else { + SetupErrorResult(result); + } + isolate->set_long_jump_base(base); + } +} + + +// Return error if isolate is in an inconsistent state. +// Return NULL when no error condition exists. +static const char* CheckIsolateState() { + if (!ClassFinalizer::FinalizePendingClasses()) { + // Make a copy of the error message as the original message string + // may get deallocated when we return back from the Dart API call. + const String& err = + String::Handle(Isolate::Current()->object_store()->sticky_error()); + const char* errmsg = err.ToCString(); + intptr_t errlen = strlen(errmsg) + 1; + char* msg = reinterpret_cast(Api::Allocate(errlen)); + OS::SNPrint(msg, errlen, "%s", errmsg); + return msg; + } + return NULL; +} + + +DART_EXPORT Dart_Result Dart_CompileAll() { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + Dart_Result result; + const char* msg = CheckIsolateState(); + if (msg != NULL) { + RETURN_FAILURE(msg); + } + CompileAll(&result); + return result; +} + + +DART_EXPORT bool Dart_IsLibrary(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + return obj.IsLibrary(); +} + + +DART_EXPORT Dart_Result Dart_LibraryUrl(Dart_Handle library) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Library& lib = Library::CheckedHandle(Api::UnwrapHandle(library)); + if (lib.IsNull()) { + RETURN_FAILURE("Null library"); + } + const String& url = String::Handle(lib.url()); + ASSERT(!url.IsNull()); + RETURN_OBJECT(url); +} + + +DART_EXPORT Dart_Result Dart_LibraryImportLibrary(Dart_Handle library_in, + Dart_Handle import_in) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Library& library = + Library::CheckedHandle(Api::UnwrapHandle(library_in)); + if (library.IsNull()) { + RETURN_FAILURE("Null library"); + } + const Library& import = + Library::CheckedHandle(Api::UnwrapHandle(import_in)); + library.AddImport(import); + RETURN_CBOOLEAN(true); +} + + +DART_EXPORT Dart_Result Dart_LookupLibrary(Dart_Handle url) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const String& url_str = String::CheckedHandle(Api::UnwrapHandle(url)); + const Library& library = Library::Handle(Library::LookupLibrary(url_str)); + if (library.IsNull()) { + RETURN_FAILURE("Unknown library"); + } else { + RETURN_OBJECT(library); + } +} + + +DART_EXPORT Dart_Result Dart_LoadLibrary(Dart_Handle url, Dart_Handle source) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const String& url_str = String::CheckedHandle(Api::UnwrapHandle(url)); + const String& source_str = String::CheckedHandle(Api::UnwrapHandle(source)); + Library& library = Library::Handle(Library::LookupLibrary(url_str)); + if (library.IsNull()) { + library = Library::New(url_str); + library.Register(); + } + Dart_Result result; + CompileSource(library, url_str, source_str, RawScript::kLibrary, &result); + return result; +} + + +DART_EXPORT Dart_Result Dart_LoadSource(Dart_Handle library_in, + Dart_Handle url_in, + Dart_Handle source_in) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const String& url = String::CheckedHandle(Api::UnwrapHandle(url_in)); + const String& source = String::CheckedHandle(Api::UnwrapHandle(source_in)); + const Library& library = + Library::CheckedHandle(Api::UnwrapHandle(library_in)); + Dart_Result result; + CompileSource(library, url, source, RawScript::kSource, &result); + return result; +} + + +DART_EXPORT Dart_Result Dart_SetNativeResolver( + Dart_Handle library, + Dart_NativeEntryResolver resolver) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Library& lib = Library::CheckedHandle(Api::UnwrapHandle(library)); + if (lib.IsNull()) { + RETURN_FAILURE("Invalid parameter, Unknown library specified"); + } + lib.set_native_entry_resolver(resolver); + RETURN_CBOOLEAN(true); +} + + +DART_EXPORT Dart_Result Dart_ObjectToString(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + Object& result = Object::Handle(); + if (obj.IsString()) { + result = obj.raw(); + } else if (obj.IsInstance()) { + Instance& receiver = Instance::Handle(); + receiver ^= obj.raw(); + result = DartLibraryCalls::ToString(receiver); + if (result.IsUnhandledException()) { + RETURN_FAILURE("An exception occurred when converting to string"); + } + } else { + // This is a VM internal object. Call the C++ method of printing. + result = String::New(obj.ToCString()); + } + RETURN_OBJECT(result); +} + + +DART_EXPORT bool Dart_IsNull(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + return obj.IsNull(); +} + + +DART_EXPORT bool Dart_IsClosure(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + return obj.IsClosure(); +} + + +DART_EXPORT Dart_Result Dart_ClosureSmrck(Dart_Handle object) { + Zone zone; + HandleScope scope; + const Closure& obj = Closure::CheckedHandle(Api::UnwrapHandle(object)); + const Integer& smrck = Integer::Handle(obj.smrck()); + RETURN_CINT64(smrck.IsNull() ? 0 : smrck.AsInt64Value()); +} + + +DART_EXPORT void Dart_ClosureSetSmrck(Dart_Handle object, int64_t value) { + Zone zone; + HandleScope scope; + const Closure& obj = Closure::CheckedHandle(Api::UnwrapHandle(object)); + const Integer& smrck = Integer::Handle(Integer::New(value)); + obj.set_smrck(smrck); +} + + +DART_EXPORT Dart_Result Dart_Objects_Equal(Dart_Handle obj1, Dart_Handle obj2) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Instance& expected = Instance::CheckedHandle(Api::UnwrapHandle(obj1)); + const Instance& actual = Instance::CheckedHandle(Api::UnwrapHandle(obj2)); + const Instance& result = + Instance::Handle(DartLibraryCalls::Equals(expected, actual)); + if (result.IsBool()) { + Bool& b = Bool::Handle(); + b ^= result.raw(); + RETURN_CBOOLEAN(b.value()); + } else { + RETURN_FAILURE("An exception occured when calling '=='"); + } +} + + +DART_EXPORT Dart_Result Dart_GetClass(Dart_Handle library, Dart_Handle name) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& param = Object::Handle(Api::UnwrapHandle(name)); + if (param.IsNull() || !param.IsString()) { + RETURN_FAILURE("Invalid class name specified"); + } + const Library& lib = Library::CheckedHandle(Api::UnwrapHandle(library)); + if (lib.IsNull()) { + RETURN_FAILURE("Invalid parameter, Unknown library specified"); + } + String& cls_name = String::Handle(); + cls_name ^= param.raw(); + const Class& cls = Class::Handle(lib.LookupClass(cls_name)); + if (cls.IsNull()) { + RETURN_FAILURE("Specified class does not exist"); + } + RETURN_OBJECT(cls); +} + + +// TODO(iposva): This call actually implements IsInstanceOfClass. +// Do we also need a real Dart_IsInstanceOf, which should take an instance +// rather than an object and a type rather than a class? +DART_EXPORT Dart_Result Dart_IsInstanceOf(Dart_Handle object, + Dart_Handle clazz) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Class& cls = Class::CheckedHandle(Api::UnwrapHandle(clazz)); + if (cls.IsNull()) { + RETURN_FAILURE("instanceof check against null class"); + } + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + Instance& instance = Instance::Handle(); + instance ^= obj.raw(); + // Finalize all classes. + const char* msg = CheckIsolateState(); + if (msg != NULL) { + RETURN_FAILURE(msg); + } + const Type& type = Type::Handle(Type::NewNonParameterizedType(cls)); + RETURN_CBOOLEAN(instance.Is(type)); +} + + +// TODO(iposva): The argument should be an instance. +DART_EXPORT bool Dart_IsNumber(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + return obj.IsNumber(); +} + + +DART_EXPORT bool Dart_IsInteger(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + return obj.IsInteger(); +} + + +DART_EXPORT Dart_Handle Dart_NewInteger(int64_t value) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Integer& obj = Integer::Handle(Integer::New(value)); + return Api::NewLocalHandle(obj); +} + + +DART_EXPORT Dart_Handle Dart_NewIntegerFromHexCString(const char* str) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const String& str_obj = String::Handle(String::New(str)); + const Integer& obj = Integer::Handle(Integer::New(str_obj)); + return Api::NewLocalHandle(obj); +} + + +DART_EXPORT Dart_Result Dart_IntegerValue(Dart_Handle integer) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(integer)); + if (obj.IsSmi() || obj.IsMint()) { + Integer& integer = Integer::Handle(); + integer ^= obj.raw(); + RETURN_CINT64(integer.AsInt64Value()); + } + if (obj.IsBigint()) { + Bigint& bigint = Bigint::Handle(); + bigint ^= obj.raw(); + if (BigintOperations::FitsIntoInt64(bigint)) { + RETURN_CINT64(BigintOperations::ToInt64(bigint)); + } else { + RETURN_CSTRING(BigintOperations::ToHexCString(bigint, &Api::Allocate)); + } + } + RETURN_FAILURE("Object is not a Integer"); +} + + +DART_EXPORT Dart_Result Dart_IntegerFitsIntoInt64(Dart_Handle integer) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(integer)); + if (obj.IsSmi() || obj.IsMint()) { + RETURN_CBOOLEAN(true); + } else if (obj.IsBigint()) { +#if defined(DEBUG) + Bigint& bigint = Bigint::Handle(); + bigint ^= obj.raw(); + ASSERT(!BigintOperations::FitsIntoInt64(bigint)); +#endif + RETURN_CBOOLEAN(false); + } + RETURN_FAILURE("Object is not a Integer"); +} + + +DART_EXPORT bool Dart_IsBoolean(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + return obj.IsBool(); +} + + +DART_EXPORT Dart_Handle Dart_NewBoolean(bool value) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Bool& obj = Bool::Handle(Bool::Get(value)); + return Api::NewLocalHandle(obj); +} + + +DART_EXPORT Dart_Result Dart_BooleanValue(Dart_Handle bool_object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(bool_object)); + if (obj.IsBool()) { + Bool& bool_obj = Bool::Handle(); + bool_obj ^= obj.raw(); + RETURN_CBOOLEAN(bool_obj.value()); + } + RETURN_FAILURE("Object is not a Boolean"); +} + + +DART_EXPORT bool Dart_IsDouble(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + return obj.IsDouble(); +} + + +DART_EXPORT Dart_Handle Dart_NewDouble(double value) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Double& obj = Double::Handle(Double::New(value)); + return Api::NewLocalHandle(obj); +} + + +DART_EXPORT Dart_Result Dart_DoubleValue(Dart_Handle integer) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(integer)); + if (obj.IsDouble()) { + Double& double_obj = Double::Handle(); + double_obj ^= obj.raw(); + RETURN_CDOUBLE(double_obj.value()); + } + RETURN_FAILURE("Object is not a Double"); +} + + +DART_EXPORT bool Dart_IsString(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + return obj.IsString(); +} + + +DART_EXPORT Dart_Result Dart_StringLength(Dart_Handle str) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(str)); + if (obj.IsString()) { + String& string_obj = String::Handle(); + string_obj ^= obj.raw(); + RETURN_CINT(string_obj.Length()); + } + RETURN_FAILURE("Object is not a String"); +} + + +DART_EXPORT Dart_Handle Dart_NewString(const char* str) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const String& obj = String::Handle(String::New(str)); + return Api::NewLocalHandle(obj); +} + + +DART_EXPORT Dart_Handle Dart_NewString8(const uint8_t* codepoints, + intptr_t length) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const String& obj = String::Handle( + String::New(reinterpret_cast(codepoints), length)); + return Api::NewLocalHandle(obj); +} + + +DART_EXPORT Dart_Handle Dart_NewString16(const uint16_t* codepoints, + intptr_t length) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const String& obj = String::Handle(String::New(codepoints, length)); + return Api::NewLocalHandle(obj); +} + + +DART_EXPORT Dart_Handle Dart_NewString32(const uint32_t* codepoints, + intptr_t length) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + UNIMPLEMENTED(); + const String& obj = String::Handle(String::New(codepoints, length)); + return Api::NewLocalHandle(obj); +} + + +DART_EXPORT bool Dart_IsString8(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + return obj.IsOneByteString(); +} + + +DART_EXPORT bool Dart_IsString16(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + return obj.IsOneByteString() || obj.IsTwoByteString(); +} + + +DART_EXPORT Dart_Result Dart_StringGet8(Dart_Handle str, + uint8_t* codepoints, + intptr_t length) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(str)); + if (obj.IsOneByteString()) { + OneByteString& string_obj = OneByteString::Handle(); + string_obj ^= obj.raw(); + intptr_t str_len = string_obj.Length(); + intptr_t copy_len = (str_len > length) ? length : str_len; + for (intptr_t i = 0; i < copy_len; i++) { + codepoints[i] = static_cast(string_obj.CharAt(i)); + } + RETURN_CINT(copy_len); + } + RETURN_FAILURE(obj.IsString() ? "Object is not a String8" : + "Object is not a String"); +} + + +DART_EXPORT Dart_Result Dart_StringGet16(Dart_Handle str, + uint16_t* codepoints, + intptr_t length) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(str)); + if (obj.IsOneByteString() || obj.IsTwoByteString()) { + String& string_obj = String::Handle(); + string_obj ^= obj.raw(); + intptr_t str_len = string_obj.Length(); + intptr_t copy_len = (str_len > length) ? length : str_len; + for (intptr_t i = 0; i < copy_len; i++) { + codepoints[i] = static_cast(string_obj.CharAt(i)); + } + RETURN_CINT(copy_len); + } + RETURN_FAILURE(obj.IsString() ? "Object is not a String16" : + "Object is not a String"); +} + + +DART_EXPORT Dart_Result Dart_StringGet32(Dart_Handle str, + uint32_t* codepoints, + intptr_t length) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(str)); + if (obj.IsString()) { + String& string_obj = String::Handle(); + string_obj ^= obj.raw(); + intptr_t str_len = string_obj.Length(); + intptr_t copy_len = (str_len > length) ? length : str_len; + for (intptr_t i = 0; i < copy_len; i++) { + codepoints[i] = static_cast(string_obj.CharAt(i)); + } + RETURN_CINT(copy_len); + } + RETURN_FAILURE("Object is not a String"); +} + + +DART_EXPORT Dart_Result Dart_StringToCString(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + if (obj.IsString()) { + const char* string_value = obj.ToCString(); + intptr_t string_length = strlen(string_value); + char* result = reinterpret_cast(malloc(string_length + 1)); + if (result == NULL) { + RETURN_FAILURE("Unable to allocate memory"); + } + strncpy(result, string_value, string_length + 1); + ASSERT(result[string_length] == '\0'); + RETURN_CSTRING(result); + } + RETURN_FAILURE("Object is not a String"); +} + + +DART_EXPORT bool Dart_IsArray(Dart_Handle object) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + return obj.IsArray(); +} + + +DART_EXPORT Dart_Handle Dart_NewArray(intptr_t length) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Array& obj = Array::Handle(Array::New(length)); + return Api::NewLocalHandle(obj); +} + + +DART_EXPORT Dart_Result Dart_GetLength(Dart_Handle array) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(array)); + if (obj.IsArray()) { + Array& array_obj = Array::Handle(); + array_obj ^= obj.raw(); + RETURN_CINT(array_obj.Length()); + } + RETURN_FAILURE("Object is not an Array"); +} + + +DART_EXPORT Dart_Result Dart_ArrayGet(Dart_Handle array, + intptr_t offset, + uint8_t* native_array, + intptr_t length) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(array)); + if (obj.IsArray()) { + Array& array_obj = Array::Handle(); + array_obj ^= obj.raw(); + if ((offset + length) <= array_obj.Length()) { + Object& element = Object::Handle(); + Integer& integer = Integer::Handle(); + for (int i = 0; i < length; i++) { + element = array_obj.At(offset + i); + integer ^= element.raw(); + native_array[i] = static_cast(integer.AsInt64Value() & 0xff); + ASSERT(integer.AsInt64Value() <= 0xff); + // TODO(hpayer): value should always be smaller then 0xff. Add error + // handling. + } + RETURN_CINT(0); + } + RETURN_FAILURE("Invalid length passed in to access array elements"); + } + RETURN_FAILURE("Object is not an Array"); +} + + +DART_EXPORT Dart_Result Dart_ArrayGetAt(Dart_Handle array, intptr_t index) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(array)); + if (obj.IsArray()) { + Array& array_obj = Array::Handle(); + array_obj ^= obj.raw(); + if ((index >= 0) && (index < array_obj.Length())) { + const Object& element = Object::Handle(array_obj.At(index)); + RETURN_OBJECT(element); + } + RETURN_FAILURE("Invalid index passed in to access array element"); + } + RETURN_FAILURE("Object is not an Array"); +} + + +DART_EXPORT Dart_Result Dart_ArraySet(Dart_Handle array, + intptr_t offset, + uint8_t* native_array, + intptr_t length) { + Zone zone; + HandleScope scope; + const Object& obj = Object::Handle(Api::UnwrapHandle(array)); + if (obj.IsArray()) { + Array& array_obj = Array::Handle(); + array_obj ^= obj.raw(); + Integer& integer = Integer::Handle(); + if ((offset + length) <= array_obj.Length()) { + for (int i = 0; i < length; i++) { + integer ^= Integer::New(native_array[i]); + array_obj.SetAt(offset + i, integer); + } + RETURN_CINT(0); + } + RETURN_FAILURE("Invalid length passed in to set array elements"); + } + RETURN_FAILURE("Object is not an Array"); +} + +DART_EXPORT Dart_Result Dart_ArraySetAt(Dart_Handle array, + intptr_t index, + Dart_Handle value) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(array)); + if (obj.IsArray()) { + Array& array_obj = Array::Handle(); + array_obj ^= obj.raw(); + const Object& value_obj = Object::Handle(Api::UnwrapHandle(value)); + if ((index >= 0) && (index < array_obj.Length())) { + array_obj.SetAt(index, value_obj); + RETURN_CINT(0); + } + RETURN_FAILURE("Invalid index passed in to set array element"); + } + RETURN_FAILURE("Object is not an Array"); +} + + +// NOTE: Need to pass 'result' as a parameter here in order to avoid +// warning: variable 'result' might be clobbered by 'longjmp' or 'vfork' +// which shows up because of the use of setjmp. +static void InvokeStatic(const Function& function, + GrowableArray& args, + Dart_Result* result) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + LongJump* base = isolate->long_jump_base(); + LongJump jump; + isolate->set_long_jump_base(&jump); + if (setjmp(*jump.Set()) == 0) { + const Instance& retval = Instance::Handle( + DartEntry::InvokeStatic(function, args)); + result->type_ = kRetObject; + result->retval_.obj_value = Api::NewLocalHandle(retval); + } else { + SetupErrorResult(result); + } + isolate->set_long_jump_base(base); +} + + +// NOTE: Need to pass 'result' as a parameter here in order to avoid +// warning: variable 'result' might be clobbered by 'longjmp' or 'vfork' +// which shows up because of the use of setjmp. +static void InvokeDynamic(const Instance& receiver, + const Function& function, + GrowableArray& args, + Dart_Result* result) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + LongJump* base = isolate->long_jump_base(); + LongJump jump; + isolate->set_long_jump_base(&jump); + if (setjmp(*jump.Set()) == 0) { + const Instance& retval = Instance::Handle( + DartEntry::InvokeDynamic(receiver, function, args)); + result->type_ = kRetObject; + result->retval_.obj_value = Api::NewLocalHandle(retval); + } else { + SetupErrorResult(result); + } + isolate->set_long_jump_base(base); +} + + +// NOTE: Need to pass 'result' as a parameter here in order to avoid +// warning: variable 'result' might be clobbered by 'longjmp' or 'vfork' +// which shows up because of the use of setjmp. +static void InvokeClosure(const Closure& closure, + GrowableArray& args, + Dart_Result* result) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + LongJump* base = isolate->long_jump_base(); + LongJump jump; + isolate->set_long_jump_base(&jump); + if (setjmp(*jump.Set()) == 0) { + const Instance& retval = Instance::Handle( + DartEntry::InvokeClosure(closure, args)); + result->type_ = kRetObject; + result->retval_.obj_value = Api::NewLocalHandle(retval); + } else { + SetupErrorResult(result); + } + isolate->set_long_jump_base(base); +} + + +DART_EXPORT Dart_Result Dart_InvokeStatic(Dart_Handle library_in, + Dart_Handle class_name_in, + Dart_Handle function_name_in, + int number_of_arguments, + Dart_Handle* arguments) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + // Finalize all classes. + const char* msg = CheckIsolateState(); + if (msg != NULL) { + RETURN_FAILURE(msg); + } + + // Now try to resolve and invoke the static function. + const Library& library = + Library::CheckedHandle(Api::UnwrapHandle(library_in)); + if (library.IsNull()) { + RETURN_FAILURE("No library specified"); + } + const String& class_name = + String::CheckedHandle(Api::UnwrapHandle(class_name_in)); + const String& function_name = + String::CheckedHandle(Api::UnwrapHandle(function_name_in)); + const Function& function = Function::Handle( + Resolver::ResolveStatic(library, + class_name, + function_name, + number_of_arguments, + Array::Handle(), // Named arguments are not yet + // supported in the API. + Resolver::kIsQualified)); + if (function.IsNull()) { + char* msg; + if (class_name.IsNull()) { + const char* format = "Unable to find entrypoint: %s()"; + intptr_t length = OS::SNPrint(NULL, 0, format, function_name.ToCString()); + msg = reinterpret_cast(Api::Allocate(length + 1)); + OS::SNPrint(msg, (length + 1), format, function_name.ToCString()); + } else { + const char* format = "Unable to find entrypoint: static %s.%s()"; + intptr_t length = OS::SNPrint(NULL, 0, format, + class_name.ToCString(), + function_name.ToCString()); + msg = reinterpret_cast(Api::Allocate(length + 1)); + OS::SNPrint(msg, (length + 1), format, + class_name.ToCString(), function_name.ToCString()); + } + RETURN_FAILURE(msg); + } + Dart_Result retval; + GrowableArray dart_arguments(number_of_arguments); + for (int i = 0; i < number_of_arguments; i++) { + const Object& arg = Object::Handle(Api::UnwrapHandle(arguments[i])); + dart_arguments.Add(&arg); + } + InvokeStatic(function, dart_arguments, &retval); + return retval; +} + + +DART_EXPORT Dart_Result Dart_InvokeDynamic(Dart_Handle object, + Dart_Handle function_name, + int number_of_arguments, + Dart_Handle* arguments) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(object)); + if (obj.IsNull()) { + RETURN_FAILURE("Null receiver passed in to invoke dynamic"); + } + if (!obj.IsInstance()) { + RETURN_FAILURE("Invalid receiver (not instance) passed to invoke dynamic"); + } + if (function_name == NULL) { + RETURN_FAILURE("Invalid function name specified"); + } + ASSERT(ClassFinalizer::AllClassesFinalized()); + + // Now try to resolve and invoke the dynamic function on this object. + Instance& receiver = Instance::Handle(); + receiver ^= obj.raw(); + const String& name = String::CheckedHandle(Api::UnwrapHandle(function_name)); + const Function& function = Function::Handle( + Resolver::ResolveDynamic(receiver, + name, + (number_of_arguments + 1), + 0)); // Named args not yet supported in API. + if (function.IsNull()) { + OS::PrintErr("Unable to find instance function: %s\n", function_name); + RETURN_FAILURE("Unable to find instance function"); + } + Dart_Result retval; + GrowableArray dart_arguments(number_of_arguments); + for (int i = 0; i < number_of_arguments; i++) { + const Object& arg = Object::Handle(Api::UnwrapHandle(arguments[i])); + dart_arguments.Add(&arg); + } + InvokeDynamic(receiver, function, dart_arguments, &retval); + return retval; +} + + +DART_EXPORT Dart_Result Dart_InvokeClosure(Dart_Handle closure, + int number_of_arguments, + Dart_Handle* arguments) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& obj = Object::Handle(Api::UnwrapHandle(closure)); + if (obj.IsNull()) { + RETURN_FAILURE("Null object passed in to invoke closure"); + } + if (!obj.IsClosure()) { + RETURN_FAILURE("Invalid closure passed to invoke closure"); + } + ASSERT(ClassFinalizer::AllClassesFinalized()); + + // Now try to invoke the closure. + Closure& closure_obj = Closure::Handle(); + closure_obj ^= obj.raw(); + Dart_Result retval; + GrowableArray dart_arguments(number_of_arguments); + for (int i = 0; i < number_of_arguments; i++) { + const Object& arg = Object::Handle(Api::UnwrapHandle(arguments[i])); + dart_arguments.Add(&arg); + } + InvokeClosure(closure_obj, dart_arguments, &retval); + return retval; +} + + +DART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args, + int index) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + NativeArguments* arguments = reinterpret_cast(args); + const Object& obj = Object::Handle(arguments->At(index)); + return Api::NewLocalHandle(obj); +} + + +DART_EXPORT void Dart_SetReturnValue(Dart_NativeArguments args, + Dart_Handle retval) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + NativeArguments* arguments = reinterpret_cast(args); + arguments->SetReturn(Object::Handle(Api::UnwrapHandle(retval))); +} + + +DART_EXPORT bool Dart_ExceptionOccurred(Dart_Handle result) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& retval = Object::Handle(Api::UnwrapHandle(result)); + return retval.IsUnhandledException(); +} + + +DART_EXPORT Dart_Result Dart_GetException(Dart_Handle result) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& retval = Object::Handle(Api::UnwrapHandle(result)); + if (retval.IsUnhandledException()) { + const UnhandledException& unhandled = UnhandledException::Handle( + reinterpret_cast(retval.raw())); + const Object& exception = Object::Handle(unhandled.exception()); + RETURN_OBJECT(exception); + } + RETURN_FAILURE("Object is not an unhandled exception object"); +} + + +DART_EXPORT Dart_Result Dart_GetStacktrace(Dart_Handle unhandled_excp) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& retval = Object::Handle(Api::UnwrapHandle(unhandled_excp)); + if (retval.IsUnhandledException()) { + const UnhandledException& unhandled = UnhandledException::Handle( + reinterpret_cast(retval.raw())); + const Object& stacktrace = Object::Handle(unhandled.stacktrace()); + RETURN_OBJECT(stacktrace); + } + RETURN_FAILURE("Object is not an unhandled exception object"); +} + + +DART_EXPORT Dart_Result Dart_ThrowException(Dart_Handle exception) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + if (isolate->top_exit_frame_info() == 0) { + // There are no dart frames on the stack so it would be illegal to + // throw an exception here. + RETURN_FAILURE("No Dart frames on stack, cannot throw exception"); + } + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Instance& excp = Instance::CheckedHandle(Api::UnwrapHandle(exception)); + // Unwind all the API scopes till the exit frame before throwing an + // exception. + ApiState* state = isolate->api_state(); + ASSERT(state != NULL); + state->UnwindScopes(isolate->top_exit_frame_info()); + Exceptions::Throw(excp); + RETURN_FAILURE("Exception was not thrown, internal error"); +} + + +DART_EXPORT Dart_Result Dart_ReThrowException(Dart_Handle exception, + Dart_Handle stacktrace) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + if (isolate->top_exit_frame_info() == 0) { + // There are no dart frames on the stack so it would be illegal to + // throw an exception here. + RETURN_FAILURE("No Dart frames on stack, cannot throw exception"); + } + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Instance& excp = Instance::CheckedHandle(Api::UnwrapHandle(exception)); + const Instance& stk = Instance::CheckedHandle(Api::UnwrapHandle(stacktrace)); + // Unwind all the API scopes till the exit frame before throwing an + // exception. + ApiState* state = isolate->api_state(); + ASSERT(state != NULL); + state->UnwindScopes(isolate->top_exit_frame_info()); + Exceptions::ReThrow(excp, stk); + RETURN_FAILURE("Exception was not re thrown, internal error"); +} + + +DART_EXPORT void Dart_EnterScope() { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ApiState* state = isolate->api_state(); + ASSERT(state != NULL); + ApiLocalScope* new_scope = new ApiLocalScope(state->top_scope(), + reinterpret_cast(&state)); + ASSERT(new_scope != NULL); + state->set_top_scope(new_scope); // New scope is now the top scope. +} + + +DART_EXPORT void Dart_ExitScope() { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ApiState* state = isolate->api_state(); + ASSERT(state != NULL); + ApiLocalScope* scope = state->top_scope(); + ASSERT(scope != NULL); + state->set_top_scope(scope->previous()); // Reset top scope to previous. + delete scope; // Free up the old scope which we have just exited. +} + + +DART_EXPORT Dart_Handle Dart_NewPersistentHandle(Dart_Handle object) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ApiState* state = isolate->api_state(); + ASSERT(state != NULL); + LocalHandle* local_ref = Api::UnwrapAsLocalHandle(*state, object); + PersistentHandle* ref = state->persistent_handles().AllocateHandle(); + ref->set_raw(*local_ref); + return reinterpret_cast(ref); +} + + +DART_EXPORT Dart_Handle Dart_MakeWeakPersistentHandle(Dart_Handle object) { + UNIMPLEMENTED(); + return NULL; +} + + +DART_EXPORT Dart_Handle Dart_MakePersistentHandle(Dart_Handle object) { + UNIMPLEMENTED(); + return NULL; +} + + +DART_EXPORT void Dart_DeletePersistentHandle(Dart_Handle object) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ApiState* state = isolate->api_state(); + ASSERT(state != NULL); + PersistentHandle* ref = Api::UnwrapAsPersistentHandle(*state, object); + ref->FreeHandle(state->persistent_handles().free_list()); + state->persistent_handles().set_free_list(ref); +} + + +static const bool kGetter = true; +static const bool kSetter = false; + + +static bool UseGetterForStaticField(const Field& fld) { + if (fld.IsNull()) { + return true; + } + + // Return getter method for uninitialized fields, rather than the + // field object, since the value in the field object will not be + // initialized until the first time the getter is invoked. + const Instance& value = Instance::Handle(fld.value()); + ASSERT(value.raw() != Object::transition_sentinel()); + return value.raw() == Object::sentinel(); +} + + +static Dart_Result LookupStaticField(Dart_Handle clazz, + Dart_Handle field_name, + bool is_getter) { + const Object& param1 = Object::Handle(Api::UnwrapHandle(clazz)); + const Object& param2 = Object::Handle(Api::UnwrapHandle(field_name)); + if (param1.IsNull() || !param1.IsClass()) { + RETURN_FAILURE("Invalid class specified"); + } + if (param2.IsNull() || !param2.IsString()) { + RETURN_FAILURE("Invalid field name specified"); + } + Class& cls = Class::Handle(); + cls ^= param1.raw(); + String& fld_name = String::Handle(); + fld_name ^= param2.raw(); + const Field& fld = Field::Handle(cls.LookupStaticField(fld_name)); + if (is_getter && UseGetterForStaticField(fld)) { + const String& func_name = String::Handle(Field::GetterName(fld_name)); + const Function& function = + Function::Handle(cls.LookupStaticFunction(func_name)); + if (!function.IsNull()) { + RETURN_OBJECT(function); + } + RETURN_FAILURE("Specified field is not found in the class"); + } + if (fld.IsNull()) { + RETURN_FAILURE("Specified field is not found in the class"); + } + RETURN_OBJECT(fld); +} + + +static Dart_Result LookupInstanceField(const Object& object, + Dart_Handle name, + bool is_getter) { + const Object& param = Object::Handle(Api::UnwrapHandle(name)); + if (param.IsNull() || !param.IsString()) { + RETURN_FAILURE("Invalid field name specified"); + } + String& field_name = String::Handle(); + field_name ^= param.raw(); + String& func_name = String::Handle(); + Field& fld = Field::Handle(); + Class& cls = Class::Handle(object.clazz()); + while (!cls.IsNull()) { + fld = cls.LookupInstanceField(field_name); + if (!fld.IsNull()) { + if (!is_getter && fld.is_final()) { + RETURN_FAILURE("Cannot set value of final fields"); + } + func_name = is_getter ? Field::GetterName(field_name) : + Field::SetterName(field_name); + const Function& function = Function::Handle( + cls.LookupDynamicFunction(func_name)); + if (function.IsNull()) { + RETURN_FAILURE("Unable to find accessor function in the class"); + } + RETURN_OBJECT(function); + } + cls = cls.SuperClass(); + } + RETURN_FAILURE("Unable to find field in the class"); +} + + +DART_EXPORT Dart_Result Dart_GetStaticField(Dart_Handle cls, + Dart_Handle name) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + Dart_Result result = LookupStaticField(cls, name, kGetter); + if (!Dart_IsValidResult(result)) { + RETURN_FAILURE(Dart_GetErrorCString(result)); + } + Object& retval = Object::Handle(); + const Object& obj = Object::Handle(Api::UnwrapHandle(Dart_GetResult(result))); + if (obj.IsField()) { + Field& fld = Field::Handle(); + fld ^= obj.raw(); + retval = fld.value(); + RETURN_OBJECT(retval); + } else { + Function& func = Function::Handle(); + func ^= obj.raw(); + GrowableArray args; + InvokeStatic(func, args, &result); + if (Dart_IsValidResult(result)) { + Dart_Handle result_obj = Dart_GetResult(result); + if (Dart_ExceptionOccurred(result_obj)) { + RETURN_FAILURE("An exception occurred when getting the static field"); + } + } + return result; + } +} + + +// TODO(iposva): The value parameter should be documented as being an instance. +DART_EXPORT Dart_Result Dart_SetStaticField(Dart_Handle cls, + Dart_Handle name, + Dart_Handle value) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + Dart_Result result = LookupStaticField(cls, name, kSetter); + if (!Dart_IsValidResult(result)) { + RETURN_FAILURE(Dart_GetErrorCString(result)); + } + Field& fld = Field::Handle(); + fld ^= Api::UnwrapHandle(Dart_GetResult(result)); + if (fld.is_final()) { + RETURN_FAILURE("Specified field is a static final field in the class"); + } + const Object& val = Object::Handle(Api::UnwrapHandle(value)); + Instance& instance = Instance::Handle(); + instance ^= val.raw(); + fld.set_value(instance); + RETURN_OBJECT(val); +} + + +DART_EXPORT Dart_Result Dart_GetInstanceField(Dart_Handle obj, + Dart_Handle name) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& param = Object::Handle(Api::UnwrapHandle(obj)); + if (param.IsNull() || !param.IsInstance()) { + RETURN_FAILURE("Invalid object passed in to access instance field"); + } + Instance& object = Instance::Handle(); + object ^= param.raw(); + Dart_Result result = LookupInstanceField(object, name, kGetter); + if (!Dart_IsValidResult(result)) { + RETURN_FAILURE(Dart_GetErrorCString(result)); + } + Function& func = Function::Handle(); + func ^= Api::UnwrapHandle(Dart_GetResult(result)); + GrowableArray arguments; + InvokeDynamic(object, func, arguments, &result); + if (Dart_IsValidResult(result)) { + Dart_Handle result_obj = Dart_GetResult(result); + if (Dart_ExceptionOccurred(result_obj)) { + RETURN_FAILURE("An exception occurred when accessing the instance field"); + } + } + return result; +} + + +DART_EXPORT Dart_Result Dart_SetInstanceField(Dart_Handle obj, + Dart_Handle name, + Dart_Handle value) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& param = Object::Handle(Api::UnwrapHandle(obj)); + if (param.IsNull() || !param.IsInstance()) { + RETURN_FAILURE("Invalid object passed in to access instance field"); + } + Instance& object = Instance::Handle(); + object ^= param.raw(); + Dart_Result result = LookupInstanceField(object, name, kSetter); + if (!Dart_IsValidResult(result)) { + RETURN_FAILURE(Dart_GetErrorCString(result)); + } + Function& func = Function::Handle(); + func ^= Api::UnwrapHandle(Dart_GetResult(result)); + GrowableArray arguments(1); + const Object& arg = Object::Handle(Api::UnwrapHandle(value)); + arguments.Add(&arg); + InvokeDynamic(object, func, arguments, &result); + if (Dart_IsValidResult(result)) { + Dart_Handle result_obj = Dart_GetResult(result); + if (Dart_ExceptionOccurred(result_obj)) { + RETURN_FAILURE("An exception occurred when setting the instance field"); + } + } + return result; +} + + +DART_EXPORT Dart_Result Dart_CreateNativeWrapperClass(Dart_Handle library, + Dart_Handle name, + int field_count) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& param = Object::Handle(Api::UnwrapHandle(name)); + if (param.IsNull() || !param.IsString() || field_count <= 0) { + RETURN_FAILURE("Invalid arguments passed to Dart_CreateNativeWrapperClass"); + } + String& cls_name = String::Handle(); + cls_name ^= param.raw(); + cls_name = String::NewSymbol(cls_name); + Library& lib = Library::Handle(); + lib ^= Api::UnwrapHandle(library); + if (lib.IsNull()) { + RETURN_FAILURE("Invalid arguments passed to Dart_CreateNativeWrapperClass"); + } + const Class& cls = Class::Handle(Class::NewNativeWrapper(&lib, + cls_name, + field_count)); + if (cls.IsNull()) { + RETURN_FAILURE("Unable to create native wrapper class : already exists"); + } + RETURN_OBJECT(cls); +} + + +DART_EXPORT Dart_Result Dart_GetNativeInstanceField(Dart_Handle obj, + int index) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& param = Object::Handle(Api::UnwrapHandle(obj)); + if (param.IsNull() || !param.IsInstance()) { + RETURN_FAILURE("Invalid object passed in to access native instance field"); + } + Instance& object = Instance::Handle(); + object ^= param.raw(); + if (!object.IsValidNativeIndex(index)) { + RETURN_FAILURE("Invalid index passed in to access native instance field"); + } + RETURN_CINT(object.GetNativeField(index)); +} + + +DART_EXPORT Dart_Result Dart_SetNativeInstanceField(Dart_Handle obj, + int index, + intptr_t value) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& param = Object::Handle(Api::UnwrapHandle(obj)); + if (param.IsNull() || !param.IsInstance()) { + RETURN_FAILURE("Invalid object passed in to set native instance field"); + } + Instance& object = Instance::Handle(); + object ^= param.raw(); + if (!object.IsValidNativeIndex(index)) { + RETURN_FAILURE("Invalid index passed in to set native instance field"); + } + object.SetNativeField(index, value); + RETURN_CINT(value); +} + + +static uint8_t* ApiAllocator(uint8_t* ptr, + intptr_t old_size, + intptr_t new_size) { + uword new_ptr = Api::Reallocate(reinterpret_cast(ptr), + old_size, + new_size); + return reinterpret_cast(new_ptr); +} + + +DART_EXPORT Dart_Result Dart_CreateSnapshot(uint8_t** snapshot_buffer, + intptr_t* snapshot_size) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + if (snapshot_buffer == NULL || snapshot_size == NULL) { + RETURN_FAILURE("Invalid input parameters to Dart_CreateSnapshot"); + } + const char* msg = CheckIsolateState(); + if (msg != NULL) { + RETURN_FAILURE(msg); + } + SnapshotWriter writer(true, snapshot_buffer, ApiAllocator); + writer.WriteFullSnapshot(); + *snapshot_size = writer.Size(); + RETURN_CBOOLEAN(true); +} + + +static uint8_t* allocator(uint8_t* ptr, intptr_t old_size, intptr_t new_size) { + void* new_ptr = realloc(reinterpret_cast(ptr), new_size); + return reinterpret_cast(new_ptr); +} + + +DART_EXPORT Dart_Result Dart_PostIntArray(Dart_Port port, intptr_t field_count, + intptr_t* data) { + uint8_t* buffer = NULL; + MessageWriter writer(&buffer, &allocator); + + writer.WriteMessage(field_count, data); + + // Post the message at the given port. The allocated message and + // buffer are deallocated after the message is processed. + PortMessage* portMessage = new PortMessage(port, 0, buffer); + bool result = PortMap::PostMessage(portMessage); + RETURN_CBOOLEAN(result); +} + + +DART_EXPORT Dart_Result Dart_Post(Dart_Port port, Dart_Handle handle) { + Zone zone; // Setup a VM zone as we are creating some handles. + HandleScope scope; // Setup a VM handle scope. + const Object& object = Object::Handle(Api::UnwrapHandle(handle)); + uint8_t* data = NULL; + SnapshotWriter writer(false, &data, &allocator); + writer.WriteObject(object.raw()); + writer.FinalizeBuffer(); + PortMessage* message = new PortMessage(port, 0, data); + bool result = PortMap::PostMessage(message); + RETURN_CBOOLEAN(result); +} + + +DART_EXPORT void Dart_InitPprofSupport() { + DebugInfo* pprof_symbol_generator = DebugInfo::NewGenerator(); + ASSERT(pprof_symbol_generator != NULL); + Dart::set_pprof_symbol_generator(pprof_symbol_generator); +} + + +DART_EXPORT void Dart_GetPprofSymbolInfo(void** buffer, int* buffer_size) { + DebugInfo* pprof_symbol_generator = Dart::pprof_symbol_generator(); + if (pprof_symbol_generator != NULL) { + ByteArray* debug_region = new ByteArray(); + ASSERT(debug_region != NULL); + pprof_symbol_generator->WriteToMemory(debug_region); + *buffer_size = debug_region->size(); + if (*buffer_size != 0) { + *buffer = reinterpret_cast(Api::Allocate(*buffer_size)); + memmove(*buffer, debug_region->data(), *buffer_size); + } else { + *buffer = NULL; + } + delete debug_region; + } else { + *buffer = NULL; + *buffer_size = 0; + } +} + + +DART_EXPORT bool Dart_IsVMFlagSet(const char* flag_name) { + if (Flags::Lookup(flag_name) != NULL) { + return true; + } + return false; +} + + +Dart_Handle Api::NewLocalHandle(const Object& object) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ApiState* state = isolate->api_state(); + ASSERT(state != NULL); + ApiLocalScope* scope = state->top_scope(); + ASSERT(scope != NULL); + LocalHandles* local_handles = scope->local_handles(); + ASSERT(local_handles != NULL); + LocalHandle* ref = local_handles->AllocateHandle(); + ref->set_raw(object); + return reinterpret_cast(ref); +} + + +RawObject* Api::UnwrapHandle(Dart_Handle object) { +#ifdef DEBUG + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ApiState* state = isolate->api_state(); + ASSERT(state != NULL); + ASSERT(state->IsValidPersistentHandle(object) || + state->IsValidLocalHandle(object)); + ASSERT(PersistentHandle::raw_offset() == 0 && + LocalHandle::raw_offset() == 0); +#endif + return *(reinterpret_cast(object)); +} + + +LocalHandle* Api::UnwrapAsLocalHandle(const ApiState& state, + Dart_Handle object) { + ASSERT(state.IsValidLocalHandle(object)); + return reinterpret_cast(object); +} + + +PersistentHandle* Api::UnwrapAsPersistentHandle(const ApiState& state, + Dart_Handle object) { + ASSERT(state.IsValidPersistentHandle(object)); + return reinterpret_cast(object); +} + + +uword Api::Allocate(intptr_t size) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ApiState* state = isolate->api_state(); + ASSERT(state != NULL); + ApiLocalScope* scope = state->top_scope(); + ASSERT(scope != NULL); + return scope->zone().Allocate(size); +} + + +uword Api::Reallocate(uword ptr, intptr_t old_size, intptr_t new_size) { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ApiState* state = isolate->api_state(); + ASSERT(state != NULL); + ApiLocalScope* scope = state->top_scope(); + ASSERT(scope != NULL); + return scope->zone().Reallocate(ptr, old_size, new_size); +} + + +} // namespace dart diff --git a/runtime/vm/dart_api_impl.h b/runtime/vm/dart_api_impl.h new file mode 100644 index 00000000000..dc2f2693a01 --- /dev/null +++ b/runtime/vm/dart_api_impl.h @@ -0,0 +1,52 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_DART_API_IMPL_H_ +#define VM_DART_API_IMPL_H_ + +#include "vm/allocation.h" + +#define RETURN_FAILURE(msg) return Dart_ErrorResult(msg) +#define RETURN_CINT(retval) return Dart_ResultAsCIntptr(retval) +#define RETURN_CBOOLEAN(retval) return Dart_ResultAsCBoolean(retval) +#define RETURN_CSTRING(retval) return Dart_ResultAsCString(retval) +#define RETURN_OBJECT(obj) return Dart_ResultAsObject(Api::NewLocalHandle(obj)) +#define RETURN_CINT64(retval) return Dart_ResultAsCInt64(retval) +#define RETURN_CDOUBLE(retval) return Dart_ResultAsCDouble(retval) + +namespace dart { + +// Forward declarations. +class Object; +class RawObject; +class LocalHandle; +class PersistentHandle; +class ApiState; + +class Api : AllStatic { + public: + // Create new local handles. + static Dart_Handle NewLocalHandle(const Object& object); + + // Unwrap the raw object from the handle. + static RawObject* UnwrapHandle(Dart_Handle object); + + // Validate and convert the passed in handle as a local handle. + static LocalHandle* UnwrapAsLocalHandle(const ApiState& state, + Dart_Handle object); + + // Validate and convert the passed in handle as a persistent handle. + static PersistentHandle* UnwrapAsPersistentHandle(const ApiState& state, + Dart_Handle object); + + // Allocate space in the local zone. + static uword Allocate(intptr_t size); + + // Reallocate space in the local zone. + static uword Reallocate(uword ptr, intptr_t old_size, intptr_t new_size); +}; + +} // namespace dart. + +#endif // VM_DART_API_IMPL_H_ diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc new file mode 100644 index 00000000000..ab6c950e7e1 --- /dev/null +++ b/runtime/vm/dart_api_impl_test.cc @@ -0,0 +1,1368 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "include/dart_api.h" + +#include "vm/assert.h" +#include "vm/dart_api_impl.h" +#include "vm/dart_api_state.h" +#include "vm/unit_test.h" +#include "vm/utils.h" +#include "vm/verifier.h" + +namespace dart { + +UNIT_TEST_CASE(BooleanValues) { + Dart_CreateIsolate(NULL, NULL); + Dart_EnterScope(); // Enter a Dart API scope for the unit test. + + Dart_Handle str = Dart_NewString("test"); + EXPECT(!Dart_IsBoolean(str)); + Dart_Handle val1 = Dart_NewBoolean(true); + EXPECT(Dart_IsBoolean(val1)); + Dart_Handle val2 = Dart_NewBoolean(false); + EXPECT(Dart_IsBoolean(val2)); + Dart_Result result = Dart_BooleanValue(val1); + EXPECT(Dart_IsValidResult(result) && Dart_GetResultAsCBoolean(result)); + result = Dart_BooleanValue(val2); + EXPECT(Dart_IsValidResult(result) && !Dart_GetResultAsCBoolean(result)); + + Dart_ExitScope(); // Exit the Dart API scope. + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(DoubleValues) { + Dart_CreateIsolate(NULL, NULL); + Dart_EnterScope(); // Enter a Dart API scope for the unit test. + + const double kDoubleVal1 = 201.29; + const double kDoubleVal2 = 101.19; + Dart_Handle val1 = Dart_NewDouble(kDoubleVal1); + EXPECT(Dart_IsDouble(val1)); + Dart_Handle val2 = Dart_NewDouble(kDoubleVal2); + EXPECT(Dart_IsDouble(val2)); + Dart_Result result = Dart_DoubleValue(val1); + EXPECT(Dart_IsValidResult(result)); + EXPECT_EQ(kDoubleVal1, Dart_GetResultAsCDouble(result)); + result = Dart_DoubleValue(val2); + EXPECT(Dart_IsValidResult(result)); + EXPECT_EQ(kDoubleVal2, Dart_GetResultAsCDouble(result)); + + Dart_ExitScope(); // Exit the Dart API scope. + Dart_ShutdownIsolate(); +} + + +#if defined(TARGET_ARCH_IA32) // only ia32 can run execution tests. + +UNIT_TEST_CASE(NumberValues) { + // TODO(antonm): add various kinds of ints (smi, mint, bigint). + const char* kScriptChars = + "class NumberValuesHelper {\n" + " static int getInt() { return 1; }\n" + " static double getDouble() { return 1.0; }\n" + " static bool getBool() { return false; }\n" + " static getNull() { return null; }\n" + "}\n"; + Dart_Result result; + Dart_Handle handle; + + Dart_CreateIsolate(NULL, NULL); + { + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + + // Create a test library and Load up a test script in it. + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + + Dart_Handle class_name = Dart_NewString("NumberValuesHelper"); + // Check int case. + result = Dart_InvokeStatic(lib, + class_name, + Dart_NewString("getInt"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + handle = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(handle)); + EXPECT(Dart_IsNumber(handle)); + + // Check double case. + result = Dart_InvokeStatic(lib, + class_name, + Dart_NewString("getDouble"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + handle = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(handle)); + EXPECT(Dart_IsNumber(handle)); + + // Check bool case. + result = Dart_InvokeStatic(lib, + class_name, + Dart_NewString("getBool"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + handle = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(handle)); + EXPECT(!Dart_IsNumber(handle)); + + // Check null case. + result = Dart_InvokeStatic(lib, + class_name, + Dart_NewString("getNull"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + handle = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(handle)); + EXPECT(!Dart_IsNumber(handle)); + + Dart_ExitScope(); // Exit the Dart API scope. + } + Dart_ShutdownIsolate(); +} + +#endif + + +UNIT_TEST_CASE(IntegerValues) { + Dart_CreateIsolate(NULL, NULL); + Dart_EnterScope(); // Enter a Dart API scope for the unit test. + + const int64_t kIntegerVal1 = 100; + const int64_t kIntegerVal2 = 0xffffffff; + const char* kIntegerVal3 = "0x123456789123456789123456789"; + + Dart_Handle val1 = Dart_NewInteger(kIntegerVal1); + EXPECT(Dart_IsInteger(val1)); + Dart_Result fits = Dart_IntegerFitsIntoInt64(val1); + EXPECT(Dart_IsValidResult(fits)); + EXPECT(Dart_GetResultAsCBoolean(fits)); + + Dart_Handle val2 = Dart_NewInteger(kIntegerVal2); + EXPECT(Dart_IsInteger(val2)); + fits = Dart_IntegerFitsIntoInt64(val2); + EXPECT(Dart_IsValidResult(fits)); + EXPECT(Dart_GetResultAsCBoolean(fits)); + + Dart_Handle val3 = Dart_NewIntegerFromHexCString(kIntegerVal3); + EXPECT(Dart_IsInteger(val3)); + fits = Dart_IntegerFitsIntoInt64(val3); + EXPECT(Dart_IsValidResult(fits)); + EXPECT(!Dart_GetResultAsCBoolean(fits)); + + Dart_Result result = Dart_IntegerValue(val1); + EXPECT(Dart_IsValidResult(result)); + EXPECT_EQ(kIntegerVal1, Dart_GetResultAsCInt64(result)); + + result = Dart_IntegerValue(val2); + EXPECT(Dart_IsValidResult(result)); + EXPECT_EQ(kIntegerVal2, Dart_GetResultAsCInt64(result)); + + result = Dart_IntegerValue(val3); + EXPECT(Dart_IsValidResult(result)); + EXPECT(!strcmp(kIntegerVal3, Dart_GetResultAsCString(result))); + + Dart_ExitScope(); // Exit the Dart API scope. + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(ArrayValues) { + Dart_CreateIsolate(NULL, NULL); + Dart_EnterScope(); // Enter a Dart API scope for the unit test. + + const int kArrayLength = 10; + Dart_Handle str = Dart_NewString("test"); + EXPECT(!Dart_IsArray(str)); + Dart_Handle val = Dart_NewArray(kArrayLength); + EXPECT(Dart_IsArray(val)); + Dart_Result result = Dart_GetLength(val); + EXPECT(Dart_IsValidResult(result)); + EXPECT_EQ(kArrayLength, Dart_GetResultAsCIntptr(result)); + + // Check invalid array access. + result = Dart_ArraySetAt(val, (kArrayLength + 10), Dart_NewInteger(10)); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_ArraySetAt(val, -10, Dart_NewInteger(10)); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_ArrayGetAt(val, (kArrayLength + 10)); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_ArrayGetAt(val, -10); + EXPECT(!Dart_IsValidResult(result)); + + for (int i = 0; i < kArrayLength; i++) { + result = Dart_ArraySetAt(val, i, Dart_NewInteger(i)); + EXPECT(Dart_IsValidResult(result)); + } + for (int i = 0; i < kArrayLength; i++) { + result = Dart_ArrayGetAt(val, i); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle val_obj = Dart_GetResult(result); + result = Dart_IntegerValue(val_obj); + EXPECT(Dart_IsValidResult(result)); + EXPECT_EQ(i, Dart_GetResultAsCInt64(result)); + } + + Dart_ExitScope(); // Exit the Dart API scope. + Dart_ShutdownIsolate(); +} + + +// Unit test for entering a scope, creating a local handle and exiting +// the scope. +UNIT_TEST_CASE(EnterExitScope) { + Dart_CreateIsolate(NULL, NULL); + Isolate* isolate = Isolate::Current(); + EXPECT(isolate != NULL); + ApiState* state = isolate->api_state(); + EXPECT(state != NULL); + ApiLocalScope* scope = state->top_scope(); + Dart_EnterScope(); + { + EXPECT(state->top_scope() != NULL); + Zone zone; + HandleScope hs; + const String& str1 = String::Handle(String::New("Test String")); + Dart_Handle ref = Api::NewLocalHandle(str1); + String& str2 = String::Handle(); + str2 ^= Api::UnwrapHandle(ref); + EXPECT(str1.Equals(str2)); + } + Dart_ExitScope(); + EXPECT(scope == state->top_scope()); + Dart_ShutdownIsolate(); +} + + +// Unit test for creating and deleting persistent handles. +UNIT_TEST_CASE(PersistentHandles) { + const char* kTestString1 = "Test String1"; + const char* kTestString2 = "Test String2"; + Dart_CreateIsolate(NULL, NULL); + Isolate* isolate = Isolate::Current(); + EXPECT(isolate != NULL); + ApiState* state = isolate->api_state(); + EXPECT(state != NULL); + ApiLocalScope* scope = state->top_scope(); + Dart_Handle handles[2000]; + Dart_EnterScope(); + { + Zone zone; + HandleScope hs; + const String& str1 = String::Handle(String::New(kTestString1)); + Dart_Handle ref1 = Api::NewLocalHandle(str1); + for (int i = 0; i < 1000; i++) { + handles[i] = Dart_NewPersistentHandle(ref1); + } + Dart_EnterScope(); + const String& str2 = String::Handle(String::New(kTestString2)); + Dart_Handle ref2 = Api::NewLocalHandle(str2); + for (int i = 1000; i < 2000; i++) { + handles[i] = Dart_NewPersistentHandle(ref2); + } + for (int i = 500; i < 1500; i++) { + Dart_DeletePersistentHandle(handles[i]); + } + for (int i = 500; i < 1000; i++) { + handles[i] = Dart_NewPersistentHandle(ref2); + } + for (int i = 1000; i < 1500; i++) { + handles[i] = Dart_NewPersistentHandle(ref1); + } + VERIFY_ON_TRANSITION; + Dart_ExitScope(); + } + Dart_ExitScope(); + { + Zone zone; + HandleScope hs; + for (int i = 0; i < 500; i++) { + String& str = String::Handle(); + str ^= Api::UnwrapHandle(handles[i]); + EXPECT(str.Equals(kTestString1, strlen(kTestString1))); + } + for (int i = 500; i < 1000; i++) { + String& str = String::Handle(); + str ^= Api::UnwrapHandle(handles[i]); + EXPECT(str.Equals(kTestString2, strlen(kTestString2))); + } + for (int i = 1000; i < 1500; i++) { + String& str = String::Handle(); + str ^= Api::UnwrapHandle(handles[i]); + EXPECT(str.Equals(kTestString1, strlen(kTestString1))); + } + for (int i = 1500; i < 2000; i++) { + String& str = String::Handle(); + str ^= Api::UnwrapHandle(handles[i]); + EXPECT(str.Equals(kTestString2, strlen(kTestString2))); + } + } + EXPECT(scope == state->top_scope()); + EXPECT_EQ(2000, state->CountPersistentHandles()); + Dart_ShutdownIsolate(); +} + + +// Unit test for creating multiple scopes and local handles within them. +// Ensure that the local handles get all cleaned out when exiting the +// scope. +UNIT_TEST_CASE(LocalHandles) { + Dart_CreateIsolate(NULL, NULL); + Isolate* isolate = Isolate::Current(); + EXPECT(isolate != NULL); + ApiState* state = isolate->api_state(); + EXPECT(state != NULL); + ApiLocalScope* scope = state->top_scope(); + Dart_Handle handles[300]; + { + Zone zone; + HandleScope hs; + Smi& val = Smi::Handle(); + + // Start a new scope and allocate some local handles. + Dart_EnterScope(); + for (int i = 0; i < 100; i++) { + val ^= Smi::New(i); + handles[i] = Api::NewLocalHandle(val); + } + EXPECT_EQ(100, state->CountLocalHandles()); + for (int i = 0; i < 100; i++) { + val ^= Api::UnwrapHandle(handles[i]); + EXPECT_EQ(i, val.Value()); + } + // Start another scope and allocate some more local handles. + { + Dart_EnterScope(); + for (int i = 100; i < 200; i++) { + val ^= Smi::New(i); + handles[i] = Api::NewLocalHandle(val); + } + EXPECT_EQ(200, state->CountLocalHandles()); + for (int i = 100; i < 200; i++) { + val ^= Api::UnwrapHandle(handles[i]); + EXPECT_EQ(i, val.Value()); + } + + // Start another scope and allocate some more local handles. + { + Dart_EnterScope(); + for (int i = 200; i < 300; i++) { + val ^= Smi::New(i); + handles[i] = Api::NewLocalHandle(val); + } + EXPECT_EQ(300, state->CountLocalHandles()); + for (int i = 200; i < 300; i++) { + val ^= Api::UnwrapHandle(handles[i]); + EXPECT_EQ(i, val.Value()); + } + EXPECT_EQ(300, state->CountLocalHandles()); + VERIFY_ON_TRANSITION; + Dart_ExitScope(); + } + EXPECT_EQ(200, state->CountLocalHandles()); + Dart_ExitScope(); + } + EXPECT_EQ(100, state->CountLocalHandles()); + Dart_ExitScope(); + } + EXPECT_EQ(0, state->CountLocalHandles()); + EXPECT(scope == state->top_scope()); + Dart_ShutdownIsolate(); +} + + +// Unit test for creating multiple scopes and allocating objects in the +// zone for the scope. Ensure that the memory is freed when the scope +// exits. +UNIT_TEST_CASE(LocalZoneMemory) { + Dart_CreateIsolate(NULL, NULL); + Isolate* isolate = Isolate::Current(); + EXPECT(isolate != NULL); + ApiState* state = isolate->api_state(); + EXPECT(state != NULL); + ApiLocalScope* scope = state->top_scope(); + { + // Start a new scope and allocate some memory. + Dart_EnterScope(); + for (int i = 0; i < 100; i++) { + Api::Allocate(16); + } + EXPECT_EQ(1600, state->ZoneSizeInBytes()); + // Start another scope and allocate some more memory. + { + Dart_EnterScope(); + for (int i = 0; i < 100; i++) { + Api::Allocate(16); + } + EXPECT_EQ(3200, state->ZoneSizeInBytes()); + { + // Start another scope and allocate some more memory. + { + Dart_EnterScope(); + for (int i = 0; i < 200; i++) { + Api::Allocate(16); + } + EXPECT_EQ(6400, state->ZoneSizeInBytes()); + Dart_ExitScope(); + } + } + EXPECT_EQ(3200, state->ZoneSizeInBytes()); + Dart_ExitScope(); + } + EXPECT_EQ(1600, state->ZoneSizeInBytes()); + Dart_ExitScope(); + } + EXPECT_EQ(0, state->ZoneSizeInBytes()); + EXPECT(scope == state->top_scope()); + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(Isolates) { + // This test currently assumes that the Dart_Isolate type is an opaque + // representation of Isolate*. + Dart_Isolate iso_1 = Dart_CreateIsolate(NULL, NULL); + EXPECT_EQ(iso_1, Isolate::Current()); + Dart_Isolate isolate = Dart_CurrentIsolate(); + EXPECT_EQ(iso_1, isolate); + Dart_ExitIsolate(); + EXPECT(NULL == Isolate::Current()); + EXPECT(NULL == Dart_CurrentIsolate()); + Dart_Isolate iso_2 = Dart_CreateIsolate(NULL, NULL); + EXPECT_EQ(iso_2, Isolate::Current()); + Dart_ExitIsolate(); + EXPECT(NULL == Isolate::Current()); + Dart_EnterIsolate(iso_2); + EXPECT_EQ(iso_2, Isolate::Current()); + Dart_ShutdownIsolate(); + EXPECT(NULL == Isolate::Current()); + Dart_EnterIsolate(iso_1); + EXPECT_EQ(iso_1, Isolate::Current()); + Dart_ShutdownIsolate(); + EXPECT(NULL == Isolate::Current()); +} + + +#if defined(TARGET_ARCH_IA32) // only ia32 can run execution tests. + +UNIT_TEST_CASE(FieldAccess) { + const char* kScriptChars = + "class Fields {\n" + " Fields(int i, int j) : fld1 = i, fld2 = j {}\n" + " int fld1;\n" + " final int fld2;\n" + " static int fld3;\n" + " static final int fld4 = 10;\n" + "}\n" + "class FieldsTest {\n" + " static Fields testMain() {\n" + " Fields obj = new Fields(10, 20);\n" + " return obj;\n" + " }\n" + "}\n"; + Dart_Result result; + + Dart_CreateIsolate(NULL, NULL); + { + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + + // Create a test library and Load up a test script in it. + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + + // Invoke a function which returns an object. + result = Dart_InvokeStatic(lib, + Dart_NewString("FieldsTest"), + Dart_NewString("testMain"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle retobj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(retobj)); + + // Now access and set various static fields of Fields class. + result = Dart_GetClass(lib, Dart_NewString("Fields")); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle cls = Dart_GetResult(result); + result = Dart_GetStaticField(cls, Dart_NewString("fld1")); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetInstanceField(retobj, Dart_NewString("fld3")); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetStaticField(cls, Dart_NewString("fld4")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(10, Dart_GetResultAsCInt64(result)); + result = Dart_SetStaticField(cls, + Dart_NewString("fld4"), + Dart_NewInteger(20)); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetStaticField(cls, Dart_NewString("fld3")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_SetStaticField(cls, + Dart_NewString("fld3"), + Dart_NewInteger(200)); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(200, Dart_GetResultAsCInt64(result)); + + // Now access and set various instance fields of the returned object. + result = Dart_GetInstanceField(retobj, Dart_NewString("fld3")); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetInstanceField(retobj, Dart_NewString("fld1")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(10, Dart_GetResultAsCInt64(result)); + result = Dart_GetInstanceField(retobj, Dart_NewString("fld2")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(20, Dart_GetResultAsCInt64(result)); + result = Dart_SetInstanceField(retobj, + Dart_NewString("fld2"), + Dart_NewInteger(40)); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_SetInstanceField(retobj, + Dart_NewString("fld1"), + Dart_NewInteger(40)); + EXPECT(Dart_IsValidResult(result)); + result = Dart_GetInstanceField(retobj, Dart_NewString("fld1")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(40, Dart_GetResultAsCInt64(result)); + + Dart_ExitScope(); // Exit the Dart API scope. + } + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(HiddenFieldAccess) { + const char* kScriptChars = + "class HiddenFields {\n" + " HiddenFields(int i, int j) : _fld1 = i, _fld2 = j {}\n" + " int _fld1;\n" + " final int _fld2;\n" + " static int _fld3;\n" + " static final int _fld4 = 10;\n" + "}\n" + "class HiddenFieldsTest {\n" + " static HiddenFields testMain() {\n" + " HiddenFields obj = new HiddenFields(10, 20);\n" + " return obj;\n" + " }\n" + "}\n"; + Dart_Result result; + + Dart_CreateIsolate(NULL, NULL); + { + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + + // Load up a test script which extends the native wrapper class. + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + + // Invoke a function which returns an object. + result = Dart_InvokeStatic(lib, + Dart_NewString("HiddenFieldsTest"), + Dart_NewString("testMain"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle retobj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(retobj)); + + // Now access and set various static fields of HiddenFields class. + result = Dart_GetClass(lib, Dart_NewString("HiddenFields")); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle cls = Dart_GetResult(result); + result = Dart_GetStaticField(cls, Dart_NewString("_fld1")); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetInstanceField(retobj, Dart_NewString("_fld3")); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetStaticField(cls, Dart_NewString("_fld4")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(10, Dart_GetResultAsCInt64(result)); + result = Dart_SetStaticField(cls, + Dart_NewString("_fld4"), + Dart_NewInteger(20)); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetStaticField(cls, Dart_NewString("_fld3")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_SetStaticField(cls, + Dart_NewString("_fld3"), + Dart_NewInteger(200)); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(200, Dart_GetResultAsCInt64(result)); + + // Now access and set various instance fields of the returned object. + result = Dart_GetInstanceField(retobj, Dart_NewString("_fld3")); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetInstanceField(retobj, Dart_NewString("_fld1")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(10, Dart_GetResultAsCInt64(result)); + result = Dart_GetInstanceField(retobj, Dart_NewString("_fld2")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(20, Dart_GetResultAsCInt64(result)); + result = Dart_SetInstanceField(retobj, + Dart_NewString("_fld2"), + Dart_NewInteger(40)); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_SetInstanceField(retobj, + Dart_NewString("_fld1"), + Dart_NewInteger(40)); + EXPECT(Dart_IsValidResult(result)); + result = Dart_GetInstanceField(retobj, Dart_NewString("_fld1")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(40, Dart_GetResultAsCInt64(result)); + + Dart_ExitScope(); // Exit the Dart API scope. + } + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(InjectNativeFields1) { + const char* kScriptChars = + "class NativeFields extends NativeFieldsWrapper {\n" + " NativeFields(int i, int j) : fld1 = i, fld2 = j {}\n" + " int fld1;\n" + " final int fld2;\n" + " static int fld3;\n" + " static final int fld4 = 10;\n" + "}\n" + "class NativeFieldsTest {\n" + " static NativeFields testMain() {\n" + " NativeFields obj = new NativeFields(10, 20);\n" + " return obj;\n" + " }\n" + "}\n"; + Dart_Result result; + + Dart_CreateIsolate(NULL, NULL); + { + Zone zone; + HandleScope scope; + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + const int kNumNativeFields = 4; + + // Create a test library. + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + + // Create a native wrapper class with native fields. + result = Dart_CreateNativeWrapperClass( + lib, + Dart_NewString("NativeFieldsWrapper"), + kNumNativeFields); + + // Load up a test script in the test library. + + // Invoke a function which returns an object of type NativeFields. + result = Dart_InvokeStatic(lib, + Dart_NewString("NativeFieldsTest"), + Dart_NewString("testMain"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle retobj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(retobj)); + Instance& obj = Instance::Handle(); + obj ^= Api::UnwrapHandle(retobj); + const Class& cls = Class::Handle(obj.clazz()); + // We expect the newly created "NativeFields" object to have + // 2 dart instance fields (fld1, fld2) and kNumNativeFields native fields. + // Hence the size of an instance of "NativeFields" should be + // (kNumNativeFields + 2) * kWordSize + sizeof the header word. + // We check to make sure the instance size computed by the VM matches + // our expectations. + EXPECT_EQ(Utils::RoundUp(((kNumNativeFields + 2) * kWordSize) + kWordSize, + kObjectAlignment), + cls.instance_size()); + + Dart_ExitScope(); // Exit the Dart API scope. + } + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(InjectNativeFields2) { + const char* kScriptChars = + "class NativeFields extends NativeFieldsWrapper {\n" + " NativeFields(int i, int j) : fld1 = i, fld2 = j {}\n" + " int fld1;\n" + " final int fld2;\n" + " static int fld3;\n" + " static final int fld4 = 10;\n" + "}\n" + "class NativeFieldsTest {\n" + " static NativeFields testMain() {\n" + " NativeFields obj = new NativeFields(10, 20);\n" + " return obj;\n" + " }\n" + "}\n"; + Dart_Result result; + + Dart_CreateIsolate(NULL, NULL); + { + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + + // Create a test library and Load up a test script in it. + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + + // Invoke a function which returns an object of type NativeFields. + result = Dart_InvokeStatic(lib, + Dart_NewString("NativeFieldsTest"), + Dart_NewString("testMain"), + 0, + NULL); + // We expect this to fail as class "NativeFields" extends + // "NativeFieldsWrapper" and there is no definition of it either + // in the dart code or through the native field injection mechanism. + EXPECT(!Dart_IsValidResult(result)); + + Dart_ExitScope(); // Exit the Dart API scope. + } + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(NativeFieldAccess) { + const char* kScriptChars = + "class NativeFields extends NativeFieldsWrapper {\n" + " NativeFields(int i, int j) : fld1 = i, fld2 = j {}\n" + " int fld1;\n" + " final int fld2;\n" + " static int fld3;\n" + " static final int fld4 = 10;\n" + "}\n" + "class NativeFieldsTest {\n" + " static NativeFields testMain() {\n" + " NativeFields obj = new NativeFields(10, 20);\n" + " return obj;\n" + " }\n" + "}\n"; + Dart_Result result; + + Dart_CreateIsolate(NULL, NULL); + { + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + const int kNumNativeFields = 4; + + // Create a test library. + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + + // Create a native wrapper class with native fields. + result = Dart_CreateNativeWrapperClass( + lib, + Dart_NewString("NativeFieldsWrapper"), + kNumNativeFields); + + // Load up a test script in it. + + // Invoke a function which returns an object of type NativeFields. + result = Dart_InvokeStatic(lib, + Dart_NewString("NativeFieldsTest"), + Dart_NewString("testMain"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle retobj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(retobj)); + + // Now access and set various instance fields of the returned object. + result = Dart_GetInstanceField(retobj, Dart_NewString("fld3")); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetInstanceField(retobj, Dart_NewString("fld1")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(10, Dart_GetResultAsCInt64(result)); + result = Dart_GetInstanceField(retobj, Dart_NewString("fld2")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(20, Dart_GetResultAsCInt64(result)); + result = Dart_SetInstanceField(retobj, + Dart_NewString("fld2"), + Dart_NewInteger(40)); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_SetInstanceField(retobj, + Dart_NewString("fld1"), + Dart_NewInteger(40)); + EXPECT(Dart_IsValidResult(result)); + result = Dart_GetInstanceField(retobj, Dart_NewString("fld1")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(40, Dart_GetResultAsCInt64(result)); + + // Now access and set various native instance fields of the returned object. + const int kNativeFld0 = 0; + const int kNativeFld1 = 1; + const int kNativeFld2 = 2; + const int kNativeFld3 = 3; + const int kNativeFld4 = 4; + result = Dart_GetNativeInstanceField(retobj, kNativeFld4); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetNativeInstanceField(retobj, kNativeFld0); + EXPECT(Dart_IsValidResult(result)); + EXPECT_EQ(0, Dart_GetResultAsCIntptr(result)); + result = Dart_GetNativeInstanceField(retobj, kNativeFld1); + EXPECT(Dart_IsValidResult(result)); + EXPECT_EQ(0, Dart_GetResultAsCIntptr(result)); + result = Dart_GetNativeInstanceField(retobj, kNativeFld2); + EXPECT(Dart_IsValidResult(result)); + EXPECT_EQ(0, Dart_GetResultAsCIntptr(result)); + result = Dart_SetNativeInstanceField(retobj, kNativeFld4, 40); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_SetNativeInstanceField(retobj, kNativeFld0, 4); + EXPECT(Dart_IsValidResult(result)); + result = Dart_SetNativeInstanceField(retobj, kNativeFld1, 40); + EXPECT(Dart_IsValidResult(result)); + result = Dart_SetNativeInstanceField(retobj, kNativeFld2, 400); + EXPECT(Dart_IsValidResult(result)); + result = Dart_SetNativeInstanceField(retobj, kNativeFld3, 4000); + EXPECT(Dart_IsValidResult(result)); + result = Dart_GetNativeInstanceField(retobj, kNativeFld3); + EXPECT(Dart_IsValidResult(result)); + EXPECT_EQ(4000, Dart_GetResultAsCIntptr(result)); + + // Now re-access various dart instance fields of the returned object + // to ensure that there was no corruption while setting native fields. + result = Dart_GetInstanceField(retobj, Dart_NewString("fld1")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(40, Dart_GetResultAsCInt64(result)); + result = Dart_GetInstanceField(retobj, Dart_NewString("fld2")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(20, Dart_GetResultAsCInt64(result)); + + Dart_ExitScope(); // Exit the Dart API scope. + } + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(NegativeNativeFieldAccess) { + const char* kScriptChars = + "class NativeFields {\n" + " NativeFields(int i, int j) : fld1 = i, fld2 = j {}\n" + " int fld1;\n" + " final int fld2;\n" + " static int fld3;\n" + " static final int fld4 = 10;\n" + "}\n" + "class NativeFieldsTest {\n" + " static NativeFields testMain1() {\n" + " NativeFields obj = new NativeFields(10, 20);\n" + " return obj;\n" + " }\n" + " static Function testMain2() {\n" + " return function() {};\n" + " }\n" + "}\n"; + Dart_Result result; + + Dart_CreateIsolate(NULL, NULL); + { + Zone zone; + HandleScope scope; + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + + // Create a test library and Load up a test script in it. + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + + // Invoke a function which returns an object of type NativeFields. + result = Dart_InvokeStatic(lib, + Dart_NewString("NativeFieldsTest"), + Dart_NewString("testMain1"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle retobj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(retobj)); + + + // Now access and set various native instance fields of the returned object. + // All of these tests are expected to return failure as there are no + // native fields in an instance of NativeFields. + const int kNativeFld0 = 0; + const int kNativeFld1 = 1; + const int kNativeFld2 = 2; + const int kNativeFld3 = 3; + const int kNativeFld4 = 4; + result = Dart_GetNativeInstanceField(retobj, kNativeFld4); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetNativeInstanceField(retobj, kNativeFld0); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetNativeInstanceField(retobj, kNativeFld1); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetNativeInstanceField(retobj, kNativeFld2); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_SetNativeInstanceField(retobj, kNativeFld4, 40); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_SetNativeInstanceField(retobj, kNativeFld3, 40); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_SetNativeInstanceField(retobj, kNativeFld0, 400); + EXPECT(!Dart_IsValidResult(result)); + + // Invoke a function which returns a closure object. + result = Dart_InvokeStatic(lib, + Dart_NewString("NativeFieldsTest"), + Dart_NewString("testMain2"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + retobj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(retobj)); + result = Dart_GetNativeInstanceField(retobj, kNativeFld4); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetNativeInstanceField(retobj, kNativeFld0); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetNativeInstanceField(retobj, kNativeFld1); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_GetNativeInstanceField(retobj, kNativeFld2); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_SetNativeInstanceField(retobj, kNativeFld4, 40); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_SetNativeInstanceField(retobj, kNativeFld3, 40); + EXPECT(!Dart_IsValidResult(result)); + result = Dart_SetNativeInstanceField(retobj, kNativeFld0, 400); + EXPECT(!Dart_IsValidResult(result)); + + Dart_ExitScope(); // Exit the Dart API scope. + } + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(GetStaticField_RunsInitializer) { + const char* kScriptChars = + "class TestClass {\n" + " static final int fld1 = 7;\n" + " static int fld2 = 11;\n" + " static void testMain() {\n" + " }\n" + "}\n"; + Dart_Result result; + + Dart_CreateIsolate(NULL, NULL); + { + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + + // Create a test library and Load up a test script in it. + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + + // Invoke a function which returns an object. + result = Dart_InvokeStatic(lib, + Dart_NewString("TestClass"), + Dart_NewString("testMain"), + 0, + NULL); + + result = Dart_GetClass(lib, Dart_NewString("TestClass")); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle cls = Dart_GetResult(result); + + // For uninitialized fields, the getter is returned + result = Dart_GetStaticField(cls, Dart_NewString("fld1")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(7, Dart_GetResultAsCInt64(result)); + + result = Dart_GetStaticField(cls, Dart_NewString("fld2")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(11, Dart_GetResultAsCInt64(result)); + + // Overwrite fld2 + result = Dart_SetStaticField(cls, + Dart_NewString("fld2"), + Dart_NewInteger(13)); + EXPECT(Dart_IsValidResult(result)); + + // We now get the new value for fld2, not the initializer + result = Dart_GetStaticField(cls, Dart_NewString("fld2")); + EXPECT(Dart_IsValidResult(result)); + result = Dart_IntegerValue(Dart_GetResult(result)); + EXPECT_EQ(13, Dart_GetResultAsCInt64(result)); + + Dart_ExitScope(); // Exit the Dart API scope. + } + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(StaticFieldNotFound) { + const char* kScriptChars = + "class TestClass {\n" + " static void testMain() {\n" + " }\n" + "}\n"; + Dart_Result result; + + Dart_CreateIsolate(NULL, NULL); + { + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + + // Create a test library and Load up a test script in it. + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + + // Invoke a function. + result = Dart_InvokeStatic(lib, + Dart_NewString("TestClass"), + Dart_NewString("testMain"), + 0, + NULL); + + result = Dart_GetClass(lib, Dart_NewString("TestClass")); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle cls = Dart_GetResult(result); + + result = Dart_GetStaticField(cls, Dart_NewString("not_found")); + EXPECT(!Dart_IsValidResult(result)); + EXPECT_STREQ("Specified field is not found in the class", + Dart_GetErrorCString(result)); + + result = Dart_SetStaticField(cls, + Dart_NewString("not_found"), + Dart_NewInteger(13)); + EXPECT(!Dart_IsValidResult(result)); + EXPECT_STREQ("Specified field is not found in the class", + Dart_GetErrorCString(result)); + + Dart_ExitScope(); // Exit the Dart API scope. + } + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(InvokeDynamic) { + const char* kScriptChars = + "class InvokeDynamic {\n" + " InvokeDynamic(int i, int j) : fld1 = i, fld2 = j {}\n" + " int method1(int i) { return i + fld1 + fld2 + fld4; }\n" + " static int method2(int i) { return i + fld4; }\n" + " int fld1;\n" + " final int fld2;\n" + " static final int fld4 = 10;\n" + "}\n" + "class InvokeDynamicTest {\n" + " static InvokeDynamic testMain() {\n" + " InvokeDynamic obj = new InvokeDynamic(10, 20);\n" + " return obj;\n" + " }\n" + "}\n"; + Dart_Result result; + + Dart_CreateIsolate(NULL, NULL); + { + Zone zone; + HandleScope scope; + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + + // Create a test library and Load up a test script in it. + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + + // Invoke a function which returns an object of type InvokeDynamic. + result = Dart_InvokeStatic(lib, + Dart_NewString("InvokeDynamicTest"), + Dart_NewString("testMain"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle retobj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(retobj)); + + + // Now invoke a dynamic method and check the result. + Dart_Handle dart_arguments[1]; + dart_arguments[0] = Dart_NewInteger(1); + result = Dart_InvokeDynamic(retobj, + Dart_NewString("method1"), + 1, + dart_arguments); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle obj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(obj)); + EXPECT(Dart_IsInteger(obj)); + result = Dart_IntegerValue(obj); + EXPECT_EQ(41, Dart_GetResultAsCInt64(result)); + + result = Dart_InvokeDynamic(retobj, Dart_NewString("method2"), 0, NULL); + EXPECT(!Dart_IsValidResult(result)); + + result = Dart_InvokeDynamic(retobj, Dart_NewString("method1"), 0, NULL); + EXPECT(!Dart_IsValidResult(result)); + + Dart_ExitScope(); // Exit the Dart API scope. + } + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(InvokeClosure) { + const char* kScriptChars = + "class InvokeClosure {\n" + " InvokeClosure(int i, int j) : fld1 = i, fld2 = j {}\n" + " Function method1(int i) {\n" + " f(int j) => j + i + fld1 + fld2 + fld4; \n" + " return f;\n" + " }\n" + " static Function method2(int i) {\n" + " n(int j) => true + i + fld4; \n" + " return n;\n" + " }\n" + " int fld1;\n" + " final int fld2;\n" + " static final int fld4 = 10;\n" + "}\n" + "class InvokeClosureTest {\n" + " static Function testMain1() {\n" + " InvokeClosure obj = new InvokeClosure(10, 20);\n" + " return obj.method1(10);\n" + " }\n" + " static Function testMain2() {\n" + " return InvokeClosure.method2(10);\n" + " }\n" + "}\n"; + Dart_Result result; + + Dart_CreateIsolate(NULL, NULL); + { + Zone zone; + HandleScope scope; + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + + // Create a test library and Load up a test script in it. + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + + // Invoke a function which returns a closure. + result = Dart_InvokeStatic(lib, + Dart_NewString("InvokeClosureTest"), + Dart_NewString("testMain1"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle retobj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(retobj)); + + EXPECT(Dart_IsClosure(retobj)); + EXPECT(!Dart_IsClosure(Dart_NewInteger(101))); + + // Now invoke the closure and check the result. + Dart_Handle dart_arguments[1]; + dart_arguments[0] = Dart_NewInteger(1); + result = Dart_InvokeClosure(retobj, 1, dart_arguments); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle obj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(obj)); + EXPECT(Dart_IsInteger(obj)); + result = Dart_IntegerValue(obj); + EXPECT_EQ(51, Dart_GetResultAsCInt64(result)); + + // Invoke closure with wrong number of args, should result in exception. + result = Dart_InvokeClosure(retobj, 0, NULL); + EXPECT(Dart_IsValidResult(result)); + obj = Dart_GetResult(result); + EXPECT(Dart_ExceptionOccurred(obj)); + + // Invoke a function which returns a closure. + result = Dart_InvokeStatic(lib, + Dart_NewString("InvokeClosureTest"), + Dart_NewString("testMain2"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + retobj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(retobj)); + + EXPECT(Dart_IsClosure(retobj)); + EXPECT(!Dart_IsClosure(Dart_NewString("abcdef"))); + + // Now invoke the closure and check the result (should be an exception). + dart_arguments[0] = Dart_NewInteger(1); + result = Dart_InvokeClosure(retobj, 1, dart_arguments); + retobj = Dart_GetResult(result); + EXPECT(Dart_ExceptionOccurred(retobj)); + + Dart_ExitScope(); // Exit the Dart API scope. + } + Dart_ShutdownIsolate(); +} + + +void ExceptionNative(Dart_NativeArguments args) { + Dart_Handle param = Dart_GetNativeArgument(args, 0); + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + char* str = reinterpret_cast(Api::Allocate(1024)); + str[0] = 0; + Dart_ThrowException(param); + UNREACHABLE(); +} + + +static Dart_NativeFunction native_lookup(Dart_Handle name, int argument_count) { + return reinterpret_cast(&ExceptionNative); +} + + +UNIT_TEST_CASE(ThrowException) { + const char* kScriptChars = + "class ThrowException {\n" + " ThrowException(int i) : fld1 = i {}\n" + " int method1(int i) native \"ThrowException_native\";" + " int method2() {\n" + " try { method1(10); } catch(var a) { return 5; } return 10;\n" + " }\n" + " int fld1;\n" + "}\n" + "class ThrowExceptionTest {\n" + " static ThrowException testMain() {\n" + " ThrowException obj = new ThrowException(10);\n" + " return obj;\n" + " }\n" + "}\n"; + Dart_Result result; + + Dart_CreateIsolate(NULL, NULL); + Isolate* isolate = Isolate::Current(); + EXPECT(isolate != NULL); + ApiState* state = isolate->api_state(); + EXPECT(state != NULL); + { + intptr_t size = state->ZoneSizeInBytes(); + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + + // Load up a test script which extends the native wrapper class. + Dart_Handle lib = TestCase::LoadTestScript( + kScriptChars, + reinterpret_cast(native_lookup)); + + // Invoke a function which returns an object of type ThrowException. + result = Dart_InvokeStatic(lib, + Dart_NewString("ThrowExceptionTest"), + Dart_NewString("testMain"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle retobj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(retobj)); + + // Throwing an exception here should result in an error. + result = Dart_ThrowException(retobj); + EXPECT(!Dart_IsValidResult(result)); + + // Now invoke method2 which invokes a natve method where it is + // ok to throw an exception, check the result which would indicate + // if an exception was thrown or not. + result = Dart_InvokeDynamic(retobj, Dart_NewString("method2"), 0, NULL); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle obj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(obj)); + EXPECT(Dart_IsInteger(obj)); + result = Dart_IntegerValue(obj); + EXPECT_EQ(5, Dart_GetResultAsCInt64(result)); + + Dart_ExitScope(); // Exit the Dart API scope. + EXPECT_EQ(size, state->ZoneSizeInBytes()); + } + Dart_ShutdownIsolate(); +} + + +UNIT_TEST_CASE(InstanceOf) { + const char* kScriptChars = + "class OtherClass {\n" + " static returnNull() { return null; }\n" + "}\n" + "class InstanceOfTest {\n" + " InstanceOfTest() {}\n" + " static InstanceOfTest testMain() {\n" + " return new InstanceOfTest();\n" + " }\n" + "}\n"; + Dart_Result result; + + Dart_CreateIsolate(NULL, NULL); + { + Dart_EnterScope(); // Start a Dart API scope for invoking API functions. + + // Create a test library and Load up a test script in it. + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + + // Invoke a function which returns an object of type InstanceOf.. + result = Dart_InvokeStatic(lib, + Dart_NewString("InstanceOfTest"), + Dart_NewString("testMain"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle instanceOfTestObj = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(instanceOfTestObj)); + + // Fetch InstanceOfTest class. + result = Dart_GetClass(lib, Dart_NewString("InstanceOfTest")); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle cls = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(cls)); + + // Now check instanceOfTestObj reported as an instance of + // InstanceOfTest class. + result = Dart_IsInstanceOf(instanceOfTestObj, cls); + EXPECT(Dart_IsValidResult(result)); + EXPECT(Dart_GetResultAsCBoolean(result)); + + // Fetch OtherClass and check if instanceOfTestObj is instance of it. + result = Dart_GetClass(lib, Dart_NewString("OtherClass")); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle otherClass = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(otherClass)); + + result = Dart_IsInstanceOf(instanceOfTestObj, otherClass); + EXPECT(Dart_IsValidResult(result)); + EXPECT(!Dart_GetResultAsCBoolean(result)); + + // Check that primitives are not instances of InstanceOfTest class. + result = Dart_IsInstanceOf(Dart_NewString("a string"), otherClass); + EXPECT(Dart_IsValidResult(result)); + EXPECT(!Dart_GetResultAsCBoolean(result)); + + result = Dart_IsInstanceOf(Dart_NewInteger(42), otherClass); + EXPECT(Dart_IsValidResult(result)); + EXPECT(!Dart_GetResultAsCBoolean(result)); + + result = Dart_IsInstanceOf(Dart_NewBoolean(true), otherClass); + EXPECT(Dart_IsValidResult(result)); + EXPECT(!Dart_GetResultAsCBoolean(result)); + + // Check that null is not an instance of InstanceOfTest class. + result = Dart_InvokeStatic(lib, + Dart_NewString("OtherClass"), + Dart_NewString("returnNull"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle null = Dart_GetResult(result); + EXPECT(!Dart_ExceptionOccurred(null)); + + result = Dart_IsInstanceOf(null, otherClass); + EXPECT(Dart_IsValidResult(result)); + EXPECT(!Dart_GetResultAsCBoolean(result)); + + // Check that error is returned if null is passed as a class argument. + result = Dart_IsInstanceOf(null, null); + EXPECT(!Dart_IsValidResult(result)); + + Dart_ExitScope(); // Exit the Dart API scope. + } + Dart_ShutdownIsolate(); +} + +#endif // TARGET_ARCH_IA32. + +} // namespace dart diff --git a/runtime/vm/dart_api_state.h b/runtime/vm/dart_api_state.h new file mode 100644 index 00000000000..26d3f9f4368 --- /dev/null +++ b/runtime/vm/dart_api_state.h @@ -0,0 +1,328 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_DART_API_STATE_H_ +#define VM_DART_API_STATE_H_ + +#include "include/dart_api.h" + +#include "vm/dart_api_impl.h" +#include "vm/flags.h" +#include "vm/handles.h" +#include "vm/object.h" +#include "vm/os.h" +#include "vm/raw_object.h" +#include "vm/visitor.h" + +#include "vm/handles_impl.h" + +namespace dart { + +// Implementation of Zone support for very fast allocation of small chunks +// of memory. The chunks cannot be deallocated individually, but instead +// zones support deallocating all chunks in one fast operation when the +// scope is exited. +class ApiZone { + public: + // Create an empty zone. + ApiZone() : zone_() { } + + // Delete all memory associated with the zone. + ~ApiZone() { } + + // Allocate 'size' bytes of memory in the zone; expands the zone by + // allocating new segments of memory on demand using 'new'. + uword Allocate(intptr_t size) { return zone_.Allocate(size); } + + // Allocate 'new_size' bytes of memory and copies 'old_size' bytes from + // 'data' into new allocated memory. Uses current zone. + uword Reallocate(uword data, intptr_t old_size, intptr_t new_size) { + return zone_.Reallocate(data, old_size, new_size); + } + + // Compute the total size of this zone. This includes wasted space that is + // due to internal fragmentation in the segments. + intptr_t SizeInBytes() const { return zone_.SizeInBytes(); } + + private: + BaseZone zone_; + + DISALLOW_COPY_AND_ASSIGN(ApiZone); +}; + + +// Implementation of local handles which are handed out from every +// dart API call, these handles are valid only in the present scope +// and are destroyed when a Dart_ExitScope() is called. +class LocalHandle { + public: + // Accessors. + RawObject* raw() const { return raw_; } + void set_raw(const Object& object) { raw_ = object.raw(); } + static intptr_t raw_offset() { return OFFSET_OF(LocalHandle, raw_); } + + private: + LocalHandle() { } + ~LocalHandle() { } + + RawObject* raw_; + DISALLOW_ALLOCATION(); // Allocated through AllocateHandle methods. + DISALLOW_COPY_AND_ASSIGN(LocalHandle); +}; + + +// Implementation of persistent handles which are handed out through the +// dart API. +class PersistentHandle { + public: + enum { + StrongReference = 0, + WeakReference, + }; + + // Accessors. + RawObject* raw() const { return raw_; } + void set_raw(const LocalHandle& ref) { raw_ = ref.raw(); } + static intptr_t raw_offset() { return OFFSET_OF(PersistentHandle, raw_); } + void* callback() const { return callback_; } + void set_callback(void* value) { callback_ = value; } + intptr_t type() const { return type_; } + void set_type(intptr_t value) { type_ = value; } + + // Overload the callback_ field as a next pointer when adding freed + // handles to the free list. + PersistentHandle* Next() { + return reinterpret_cast(callback_); + } + void SetNext(PersistentHandle* free_list) { + callback_ = reinterpret_cast(free_list); + } + void FreeHandle(PersistentHandle* free_list) { + raw_ = NULL; + SetNext(free_list); + } + + private: + PersistentHandle() { } + ~PersistentHandle() { } + + RawObject* raw_; + void* callback_; + intptr_t type_; + DISALLOW_ALLOCATION(); // Allocated through AllocateHandle methods. + DISALLOW_COPY_AND_ASSIGN(PersistentHandle); +}; + + +// Local handles repository structure. +static const int kLocalHandleSizeInWords = sizeof(LocalHandle) / kWordSize; +static const int kLocalHandlesPerChunk = 64; +static const int kOffsetOfRawPtrInLocalHandle = 0; +class LocalHandles : Handles { + public: + LocalHandles() : Handles() { } + ~LocalHandles() { } + + + // Visit all object pointers stored in the various handles. + void VisitObjectPointers(ObjectPointerVisitor* visitor) { + Handles::VisitObjectPointers(visitor); + } + + // Allocates a handle in the current handle scope. This handle is valid only + // in the current handle scope and is destroyed when the current handle + // scope ends. + LocalHandle* AllocateHandle() { + return reinterpret_cast(AllocateScopedHandle()); + } + + // Validate if passed in handle is a Local Handle. + bool IsValidHandle(Dart_Handle object) const { + return IsValidScopedHandle(reinterpret_cast(object)); + } + + // Returns a count of active handles (used for testing purposes). + int CountHandles() const { + return CountScopedHandles(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(LocalHandles); +}; + + +// Persistent handles repository structure. +static const int kPersistentHandleSizeInWords = + sizeof(PersistentHandle) / kWordSize; +static const int kPersistentHandlesPerChunk = 64; +static const int kOffsetOfRawPtrInPersistentHandle = 0; +class PersistentHandles : Handles { + public: + PersistentHandles() : Handles(), + free_list_(NULL) { } + ~PersistentHandles() { + free_list_ = NULL; + } + + // Accessors. + PersistentHandle* free_list() const { return free_list_; } + void set_free_list(PersistentHandle* value) { free_list_ = value; } + + // Visit all object pointers stored in the various handles. + void VisitObjectPointers(ObjectPointerVisitor* visitor) { + Handles::VisitObjectPointers(visitor); + } + + // Allocates a persistent handle, these have to be destroyed explicitly + // by calling FreeHandle. + PersistentHandle* AllocateHandle() { + PersistentHandle* handle; + if (free_list_ != NULL) { + handle = free_list_; + free_list_ = handle->Next(); + } else { + handle = reinterpret_cast(AllocateScopedHandle()); + } + handle->set_callback(NULL); + handle->set_type(PersistentHandle::StrongReference); + return handle; + } + + // Validate if passed in handle is a Persistent Handle. + bool IsValidHandle(Dart_Handle object) const { + return IsValidScopedHandle(reinterpret_cast(object)); + } + + // Returns a count of active handles (used for testing purposes). + int CountHandles() const { + return CountScopedHandles(); + } + + private: + PersistentHandle* free_list_; + DISALLOW_COPY_AND_ASSIGN(PersistentHandles); +}; + + +// Structure used for the implementation of local scopes used in dart_api. +// These local scopes manage handles and memory allocated in the scope. +class ApiLocalScope { + public: + ApiLocalScope(ApiLocalScope* previous, uword stack_marker) : + previous_(previous), stack_marker_(stack_marker) { } + ~ApiLocalScope() { + previous_ = NULL; + } + + // Accessors. + ApiLocalScope* previous() const { return previous_; } + uword stack_marker() const { return stack_marker_; } + void set_previous(ApiLocalScope* value) { previous_ = value; } + LocalHandles* local_handles() { return &local_handles_; } + ApiZone& zone() { return zone_; } + + private: + ApiLocalScope* previous_; + uword stack_marker_; + LocalHandles local_handles_; + ApiZone zone_; + + DISALLOW_COPY_AND_ASSIGN(ApiLocalScope); +}; + + +// Implementation of the API State used in dart api for maintaining +// local scopes, persistent handles etc. These are setup on a per isolate +// basis and destroyed when the isolate is shutdown. +class ApiState { + public: + ApiState() : top_scope_(NULL) { } + ~ApiState() { + while (top_scope_ != NULL) { + ApiLocalScope* scope = top_scope_; + top_scope_ = top_scope_->previous(); + delete scope; + } + } + + // Accessors. + ApiLocalScope* top_scope() const { return top_scope_; } + void set_top_scope(ApiLocalScope* value) { top_scope_ = value; } + PersistentHandles& persistent_handles() { return persistent_handles_; } + + void UnwindScopes(uword sp) { + while (top_scope_ != NULL && top_scope_->stack_marker() < sp) { + ApiLocalScope* scope = top_scope_; + top_scope_ = top_scope_->previous(); + delete scope; + } + } + + void VisitObjectPointers(ObjectPointerVisitor* visitor) { + ApiLocalScope* scope = top_scope_; + while (scope != NULL) { + scope->local_handles()->VisitObjectPointers(visitor); + scope = scope->previous(); + } + persistent_handles().VisitObjectPointers(visitor); + } + + bool IsValidLocalHandle(Dart_Handle object) const { + ApiLocalScope* scope = top_scope_; + while (scope != NULL) { + if (scope->local_handles()->IsValidHandle(object)) { + return true; + } + scope = scope->previous(); + } + return false; + } + bool IsValidPersistentHandle(Dart_Handle object) const { + return persistent_handles_.IsValidHandle(object); + } + + int CountLocalHandles() const { + int total = 0; + ApiLocalScope* scope = top_scope_; + while (scope != NULL) { + total += scope->local_handles()->CountHandles(); + scope = scope->previous(); + } + return total; + } + int CountPersistentHandles() const { + return persistent_handles_.CountHandles(); + } + int ZoneSizeInBytes() const { + int total = 0; + ApiLocalScope* scope = top_scope_; + while (scope != NULL) { + total += scope->zone().SizeInBytes(); + scope = scope->previous(); + } + return total; + } + + private: + PersistentHandles persistent_handles_; + ApiLocalScope* top_scope_; + + DISALLOW_COPY_AND_ASSIGN(ApiState); +}; + +} // namespace dart + +#endif // VM_DART_API_STATE_H_ diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc new file mode 100644 index 00000000000..f22489b2314 --- /dev/null +++ b/runtime/vm/dart_entry.cc @@ -0,0 +1,173 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/dart_entry.h" + +#include "vm/code_generator.h" +#include "vm/compiler.h" +#include "vm/longjump.h" +#include "vm/object_store.h" +#include "vm/resolver.h" +#include "vm/stub_code.h" + +namespace dart { + +RawInstance* DartEntry::InvokeDynamic( + const Instance& receiver, + const Function& function, + const GrowableArray& arguments) { + // Get the entrypoint corresponding to the function specified, this + // will result in a compilation of the function if it is not already + // compiled. + if (!function.HasCode()) { + Compiler::CompileFunction(function); + } + const Code& code = Code::Handle(function.code()); + ASSERT(!code.IsNull()); + const Instructions& instrs = Instructions::Handle(code.instructions()); + ASSERT(!instrs.IsNull()); + + // Set up arguments to include the receiver as the first argument. + int number_of_args = arguments.length() + 1; + GrowableArray args(number_of_args); + const Object& arg0 = Object::ZoneHandle(receiver.raw()); + args.Add(&arg0); + for (int i = 0; i < arguments.length(); i++) { + args.Add(arguments[i]); + } + + // Now Call the invoke stub which will invoke the dart function. + invokestub entrypoint = reinterpret_cast( + StubCode::InvokeDartCodeEntryPoint()); + const Context& context = + Context::ZoneHandle(Isolate::Current()->object_store()->empty_context()); + ASSERT(context.isolate() == Isolate::Current()); + return entrypoint(instrs.EntryPoint(), + CodeGenerator::ArgumentsDescriptor(number_of_args, NULL), + args.data(), + context); +} + + +RawInstance* DartEntry::InvokeStatic( + const Function& function, const GrowableArray& arguments) { + // Get the entrypoint corresponding to the function specified, this + // will result in a compilation of the function if it is not already + // compiled. + ASSERT(!function.IsNull()); + if (!function.HasCode()) { + Compiler::CompileFunction(function); + } + const Code& code = Code::Handle(function.code()); + ASSERT(!code.IsNull()); + const Instructions& instrs = Instructions::Handle(code.instructions()); + ASSERT(!instrs.IsNull()); + + // Now Call the invoke stub which will invoke the dart function. + invokestub entrypoint = reinterpret_cast( + StubCode::InvokeDartCodeEntryPoint()); + const Context& context = + Context::ZoneHandle(Isolate::Current()->object_store()->empty_context()); + ASSERT(context.isolate() == Isolate::Current()); + return entrypoint( + instrs.EntryPoint(), + CodeGenerator::ArgumentsDescriptor(arguments.length(), NULL), + arguments.data(), + context); +} + + +RawInstance* DartEntry::InvokeClosure( + const Closure& closure, const GrowableArray& arguments) { + // Get the entrypoint corresponding to the closure specified, this + // will result in a compilation of the closure if it is not already + // compiled. + ASSERT(Class::Handle(closure.clazz()).signature_function() != Object::null()); + const Function& function = Function::Handle(closure.function()); + const Context& context = Context::Handle(closure.context()); + ASSERT(!function.IsNull()); + if (!function.HasCode()) { + Compiler::CompileFunction(function); + } + const Code& code = Code::Handle(function.code()); + ASSERT(!code.IsNull()); + const Instructions& instrs = Instructions::Handle(code.instructions()); + ASSERT(!instrs.IsNull()); + + // Now Call the invoke stub which will invoke the closure. + invokestub entrypoint = reinterpret_cast( + StubCode::InvokeDartCodeEntryPoint()); + ASSERT(context.isolate() == Isolate::Current()); + return entrypoint( + instrs.EntryPoint(), + CodeGenerator::ArgumentsDescriptor(arguments.length(), NULL), + arguments.data(), + context); +} + + +RawInstance* DartLibraryCalls::ExceptionCreate( + const String& class_name, + const GrowableArray& arguments) { + const Library& core_lib = Library::Handle(Library::CoreLibrary()); + const Class& cls = Class::Handle(core_lib.LookupClass(class_name)); + ASSERT(!cls.IsNull()); + // For now, we only support a non-parameterized or raw type. + const Instance& exception_object = Instance::Handle(Instance::New(cls)); + GrowableArray constructor_arguments(arguments.length() + 1); + constructor_arguments.Add(&exception_object); + constructor_arguments.AddArray(arguments); + + const String& period = String::Handle(String::New(".")); + String& constructor_name = String::Handle(String::Concat(class_name, period)); + Function& constructor = + Function::Handle(cls.LookupConstructor(constructor_name)); + ASSERT(!constructor.IsNull()); + DartEntry::InvokeStatic(constructor, constructor_arguments); + return exception_object.raw(); +} + + +RawInstance* DartLibraryCalls::ToString(const Instance& receiver) { + const String& function_name = + String::Handle(String::NewSymbol("toString")); + GrowableArray arguments; + const int kNumArguments = 1; // Receiver. + const int kNumNamedArguments = 0; // None. + const Function& function = Function::Handle( + Resolver::ResolveDynamic(receiver, + function_name, + kNumArguments, + kNumNamedArguments)); + ASSERT(!function.IsNull()); + const Instance& result = Instance::Handle( + DartEntry::InvokeDynamic(receiver, function, arguments)); + // Object's 'toString' threw an exception, let the caller handle it. + ASSERT(result.IsString() || result.IsUnhandledException()); + return result.raw(); +} + + +RawInstance* DartLibraryCalls::Equals(const Instance& left, + const Instance& right) { + const String& function_name = + String::Handle(String::NewSymbol("==")); + GrowableArray arguments; + arguments.Add(&right); + const int kNumArguments = 2; + const int kNumNamedArguments = 0; + const Function& function = Function::Handle( + Resolver::ResolveDynamic(left, + function_name, + kNumArguments, + kNumNamedArguments)); + ASSERT(!function.IsNull()); + const Instance& result = Instance::Handle( + DartEntry::InvokeDynamic(left, function, arguments)); + // Object's '==' threw an exception, let the caller handle it. + ASSERT(result.IsBool() || result.IsUnhandledException()); + return result.raw(); +} + +} // namespace dart diff --git a/runtime/vm/dart_entry.h b/runtime/vm/dart_entry.h new file mode 100644 index 00000000000..592a82bdde1 --- /dev/null +++ b/runtime/vm/dart_entry.h @@ -0,0 +1,65 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_DART_ENTRY_H_ +#define VM_DART_ENTRY_H_ + +#include "vm/allocation.h" +#include "vm/growable_array.h" + +namespace dart { + +// Forward declarations. +class Array; +class Closure; +class Context; +class Function; +class Instance; +class Integer; +class Object; +class RawInstance; +class String; + +// DartEntry abstracts functionality needed to resolve dart functions +// and invoke them from C++. +class DartEntry : public AllStatic { + public: + typedef RawInstance* (*invokestub)(uword entry_point, + const Array& arguments_descriptor, + const Object** arguments, + const Context& context); + + // Invoke the specified instance function on the receiver. + // Returns object returned by the dart instance function. + static RawInstance* InvokeDynamic( + const Instance& receiver, + const Function& function, + const GrowableArray& arguments); + + // Invoke the specified static function. + // Returns object returned by the dart static function. + static RawInstance* InvokeStatic( + const Function& function, const GrowableArray& arguments); + + // Invoke the specified closure object. + // Returns object returned by the closure. + static RawInstance* InvokeClosure( + const Closure& closure, const GrowableArray& arguments); +}; + + +// Utility functions to call from VM into Dart bootstrap libraries. +// Each may return an exception object. +class DartLibraryCalls : public AllStatic { + public: + static RawInstance* ExceptionCreate( + const String& exception_name, + const GrowableArray& arguments); + static RawInstance* ToString(const Instance& receiver); + static RawInstance* Equals(const Instance& left, const Instance& right); +}; + +} // namespace dart + +#endif // VM_DART_ENTRY_H_ diff --git a/runtime/vm/dart_entry_test.cc b/runtime/vm/dart_entry_test.cc new file mode 100644 index 00000000000..8248df71399 --- /dev/null +++ b/runtime/vm/dart_entry_test.cc @@ -0,0 +1,44 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assembler.h" +#include "vm/assert.h" +#include "vm/compiler.h" +#include "vm/dart_entry.h" +#include "vm/object.h" +#include "vm/resolver.h" +#include "vm/unit_test.h" + +namespace dart { + +#if defined(TARGET_ARCH_IA32) // only ia32 can run execution tests. + +TEST_CASE(DartEntry) { + const char* kScriptChars = + "class A {\n" + " static foo() { return 42; }\n" + "}\n"; + String& url = String::Handle(String::New("dart-test:DartEntry")); + String& source = String::Handle(String::New(kScriptChars)); + Script& script = Script::Handle(Script::New(url, source, RawScript::kScript)); + Library& lib = Library::Handle(Library::CoreLibrary()); + EXPECT_EQ(true, CompilerTest::TestCompileScript(lib, script)); + Class& cls = Class::Handle( + lib.LookupClass(String::Handle(String::NewSymbol("A")))); + EXPECT(!cls.IsNull()); + String& name = String::Handle(String::New("foo")); + Function& function = Function::Handle(cls.LookupStaticFunction(name)); + EXPECT(!function.IsNull()); + + EXPECT(CompilerTest::TestCompileFunction(function)); + EXPECT(function.HasCode()); + GrowableArray arguments; + const Smi& retval = Smi::Handle( + reinterpret_cast(DartEntry::InvokeStatic(function, arguments))); + EXPECT_EQ(Smi::New(42), retval.raw()); +} + +#endif // TARGET_ARCH_IA32. + +} // namespace dart diff --git a/runtime/vm/debuginfo.h b/runtime/vm/debuginfo.h new file mode 100644 index 00000000000..2b4bc8658ad --- /dev/null +++ b/runtime/vm/debuginfo.h @@ -0,0 +1,103 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_DEBUGINFO_H_ +#define VM_DEBUGINFO_H_ + +#include "vm/assert.h" +#include "vm/globals.h" +#include "vm/utils.h" + +namespace dart { + +// A basic ByteArray which is growable and uses malloc/free. +class ByteArray { + public: + ByteArray() : size_(0), capacity_(0), data_(NULL) { } + ~ByteArray() { + free(data_); + size_ = 0; + capacity_ = 0; + data_ = NULL; + } + + uint8_t at(int index) const { + ASSERT(0 <= index); + ASSERT(index < size_); + ASSERT(size_ <= capacity_); + return data_[index]; + } + + uint8_t* data() const { return data_; } + void set_data(uint8_t* value) { data_ = value; } + int size() const { return size_; } + + // Append an element. + void Add(const uint8_t value) { + Resize(size() + 1); + data_[size() - 1] = value; + } + + private: + void Resize(int new_size) { + if (new_size > capacity_) { + int new_capacity = Utils::RoundUpToPowerOfTwo(new_size); + uint8_t* new_data = + reinterpret_cast(realloc(data_, new_capacity)); + ASSERT(new_data != NULL); + data_ = new_data; + capacity_ = new_capacity; + } + size_ = new_size; + } + + int size_; + int capacity_; + uint8_t* data_; + + // Disallow assignment + DISALLOW_COPY_AND_ASSIGN(ByteArray); +}; + + +// DebugInfo is used to generate minimal debug information containing code, +// symbols, and line numbers for generated code in the dart VM. This information +// can be used in two ways: +// - for debugging using a debugger +// - for generating information to be read by pprof to analyze Dart programs. +class DebugInfo { + public: + ~DebugInfo(); + + // Add the code starting at pc. + void AddCode(uword pc, intptr_t size); + + // Add symbol information for a region (includes the start and end symbol), + // does not add the actual code. + void AddCodeRegion(const char* name, uword pc, intptr_t size); + + // Write out all the debug information info the memory region. + bool WriteToMemory(ByteArray* region); + + // Create a new debug information generator. + static DebugInfo* NewGenerator(); + + // Register this generated section with debuggger using the JIT interface. + static void RegisterSection(const char* name, + uword entry_point, + intptr_t size); + + // Unregister all generated section from debuggger. + static void UnregisterAllSections(); + + private: + void* handle_; + DebugInfo(); + + DISALLOW_COPY_AND_ASSIGN(DebugInfo); +}; + +} // namespace dart + +#endif // VM_DEBUGINFO_H_ diff --git a/runtime/vm/debuginfo_linux.cc b/runtime/vm/debuginfo_linux.cc new file mode 100644 index 00000000000..323744ae0ef --- /dev/null +++ b/runtime/vm/debuginfo_linux.cc @@ -0,0 +1,549 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/debuginfo.h" + +#include "vm/gdbjit_linux.h" +#include "vm/os.h" +#include "vm/utils.h" +#include "vm/thread.h" + +namespace dart { + +// ----------------------------------------------------------------------------- +// Implementation of ElfGen +// +// Specification documents: +// http://refspecs.freestandards.org +// +// ELF generic ABI: +// http://refspecs.freestandards.org/elf/gabi4+/contents.html +// ELF processor-specific supplement for X86_64: +// http://refspecs.freestandards.org/elf/x86_64-SysV-psABI.pdf +// DWARF 2.0: +// http://refspecs.freestandards.org/dwarf/dwarf-2.0.0.pdf + +// Forward declarations. +class File; + +// ElfGen is used to generate minimal ELF information containing code, symbols, +// and line numbers for generated code in the dart VM. This information is +// used in two ways: +// - it is used to generate in-memory ELF information which is then +// registered with gdb using the JIT interface. +// - it is also used to generate a file with the ELF information. This file +// is not executed, but read by pprof to analyze Dart programs. + +class ElfGen { + public: + ElfGen(); + ~ElfGen(); + + // Add the code starting at pc. + void AddCode(uword pc, intptr_t size); + + // Add symbol information for a region (includes the start and end symbol), + // does not add the actual code. + void AddCodeRegion(const char* name, uword pc, intptr_t size); + + // Add specified symbol information, does not add the actual code. + int AddFunction(const char* name, uword pc, intptr_t size); + + // Write out all the Elf information using the specified handle. + bool WriteToFile(File* handle); + bool WriteToMemory(ByteArray* region); + + // Register this generated section with GDB using the JIT interface. + static void RegisterSectionWithGDB(const char* name, + uword entry_point, + intptr_t size); + + // Unregister all generated section from GDB. + static void UnregisterAllSectionsWithGDB(); + + private: + // ELF helpers + typedef int (*OutputWriter)(void* handle, const ByteArray& section); + typedef void (*OutputPadder)(void* handle, int padding_size); + + int AddString(ByteArray* buf, const char* str); + int AddSectionName(const char* str); + int AddName(const char* str); + void AddELFHeader(int shoff); + void AddSectionHeader(int section, int offset); + int PadSection(ByteArray* section, int offset, int alignment); + bool WriteOutput(void* handle, OutputWriter writer, OutputPadder padder); + + uword text_vma_; // text section vma + intptr_t text_size_; // text section size + int text_padding_; // padding preceding text section + + static const int kNumSections = 5; // we generate 5 sections + int section_name_[kNumSections]; // array of section name indices + ByteArray section_buf_[kNumSections]; // array of section buffers + ByteArray header_; // ELF header buffer + ByteArray sheaders_; // section header table buffer + ByteArray lineprog_; // line statement program, part of '.debug_line' section + + // current state of the DWARF line info generator + uintptr_t cur_addr_; // current pc + int map_offset_; + uword map_begin_; + uword map_end_; + + Mutex lock_; +}; + + +enum { + // Various constant sizes for ELF files. + kAddrSize = sizeof(uword), + kPageSize = 4*1024, // Memory mapping page size. + kTextAlign = 16, + kELFHeaderSize = 40 + 3*kAddrSize, + kProgramHeaderEntrySize = 8 + 6*kAddrSize, + kSectionHeaderEntrySize = 16 + 6*kAddrSize, + kSymbolSize = 8 + 2*kAddrSize, + + // Our own layout of sections. + kUndef = 0, // Undefined section. + kText, // Text section. + kShStrtab, // Section header string table. + kStrtab, // String table. + kSymtab, // Symbol table. + kNumSections, // Num of section header entries in section header table. + + // Various ELF constants. + kELFCLASS32 = 1, + kELFCLASS64 = 2, + kELFDATA2LSB = 1, + kELFDATA2MSB = 2, + kEM_386 = 3, + kEM_ARM = 40, + kEM_X86_64 = 62, + kEV_CURRENT = 1, + kET_EXEC = 2, // not used + kET_DYN = 3, + kSHT_PROGBITS = 1, + kSHT_SYMTAB = 2, + kSHT_STRTAB = 3, + kSHF_WRITE = 1, // not used + kSHF_ALLOC = 2, + kSHF_EXECINSTR = 4, + kSTB_LOCAL = 0, + kSTB_EXPORTED = 1, + kSTT_FUNC = 2, +}; + + +// ELF and DWARF constants. +static const char* kEI_MAG0_MAG3 = "\177ELF"; +static const uint8_t kSpecialOpcodeLengths[] = { 0, 1, 1, 1, 1, 0, 0, 0, 1 }; + + +// Section attributes. +// The field names correspond to the field names of Elf32_Shdr and Elf64_Shdr. +static const struct { + // Section header index (only used to check correct section order). + int shndx; + const char* name; // sh_name will be the index of name inserted in shstrtab. + int sh_type; + int sh_flags; + int sh_link; + int sh_addralign; + int sh_entsize; +} section_attr[kNumSections + 1] = { + { kUndef, "", 0, 0, + 0, 0, 0 }, + { kText, ".text", kSHT_PROGBITS, kSHF_ALLOC|kSHF_EXECINSTR, + 0, kTextAlign, 0 }, + { kShStrtab, ".shstrtab", kSHT_STRTAB, 0, + 0, 1, 0 }, + { kStrtab, ".strtab", kSHT_STRTAB, 0, + 0, 1, 0 }, + { kSymtab, ".symtab", kSHT_SYMTAB, 0, + kStrtab, kAddrSize, kSymbolSize }, + // Sentinel to pad the last section + // for proper alignment of section header table. + { 0, "", 0, 0, + 0, kAddrSize, 0 } +}; + + +// Convenience function aligning an integer. +static inline uintptr_t Align(uintptr_t x, intptr_t size) { + // size is a power of 2 + ASSERT((size & (size-1)) == 0); + return (x + (size-1)) & ~(size-1); +} + + +// Convenience function writing a single byte to a ByteArray. +static inline void WriteByte(ByteArray* buf, uint8_t byte) { + buf->Add(byte); +} + + +// Convenience function writing an unsigned native word to a ByteArray. +// The word is 32-bit wide in 32-bit mode and 64-bit wide in 64-bit mode. +static inline void WriteWord(ByteArray* buf, uword word) { + uint8_t* p = reinterpret_cast(&word); + for (size_t i = 0; i < sizeof(word); i++) { + buf->Add(p[i]); + } +} + +static inline void WriteInt(ByteArray* buf, int word) { + uint8_t* p = reinterpret_cast(&word); + for (size_t i = 0; i < sizeof(word); i++) { + buf->Add(p[i]); + } +} + +static inline void WriteShort(ByteArray* buf, uint16_t word) { + uint8_t* p = reinterpret_cast(&word); + for (size_t i = 0; i < sizeof(word); i++) { + buf->Add(p[i]); + } +} + +static inline void WriteString(ByteArray* buf, const char* str) { + for (size_t i = 0; i < strlen(str); i++) { + buf->Add(static_cast(str[i])); + } +} + +static inline void Write(ByteArray* buf, const void* mem, int length) { + const uint8_t* p = reinterpret_cast(mem); + for (int i = 0; i < length; i++) { + buf->Add(p[i]); + } +} + + +// Write given section to file and return written size. +static int WriteSectionToFile(void* handle, const ByteArray& section) { +#if 0 + File* fp = reinterpret_cast(handle); + int size = section.size(); + fp->WriteFully(section.data(), size); + return size; +#else + return 0; +#endif +} + + +// Pad output file to specified padding size. +static void PadFile(void* handle, int padding_size) { +#if 0 + File* fp = reinterpret_cast(handle); + for (int i = 0; i < padding_size; i++) { + fp->WriteFully("", 1); + } +#endif +} + + +// Write given section to specified memory region and return written size. +static int WriteSectionToMemory(void* handle, const ByteArray& section) { + ByteArray* buffer = reinterpret_cast(handle); + int size = section.size(); + for (int i = 0; i < size; i++) { + buffer->Add(static_cast(section.data()[i])); + } + return size; +} + + +// Pad memory to specified padding size. +static void PadMemory(void* handle, int padding_size) { + ByteArray* buffer = reinterpret_cast(handle); + for (int i = 0; i < padding_size; i++) { + buffer->Add(static_cast(0)); + } +} + + +// Constructor +ElfGen::ElfGen() + : text_vma_(0), text_size_(0), text_padding_(0), map_offset_(0), lock_() { + for (int i = 0; i < kNumSections; i++) { + ASSERT(section_attr[i].shndx == i); // Verify layout of sections. + section_name_[i] = AddSectionName(section_attr[i].name); + } + // Section header string table always starts with an empty string, which is + // the name of the kUndef section. + ASSERT((section_attr[0].name[0] == '\0') && (section_name_[0] == 0)); + + // String table always starts with an empty string. + AddName(""); + ASSERT(section_buf_[kStrtab].size() == 1); + + // Symbol at index 0 in symtab is always STN_UNDEF (all zero): + ByteArray* symtab = §ion_buf_[kSymtab]; + while (symtab->size() < kSymbolSize) { + WriteInt(symtab, 0); + } + ASSERT(symtab->size() == kSymbolSize); +} + + +// Destructor +ElfGen::~ElfGen() { +} + + +void ElfGen::AddCode(uword pc, intptr_t size) { + MutexLocker ml(&lock_); + text_vma_ = pc; + text_size_ = size; + // We pad the text section in the file to align absolute code addresses with + // corresponding file offsets as if the code had been loaded by memory + // mapping. + if (text_vma_ % kPageSize < kELFHeaderSize) { + text_padding_ = text_vma_ % kPageSize + kPageSize - kELFHeaderSize; + } else { + text_padding_ = text_vma_ % kPageSize - kELFHeaderSize; + } + + Write(§ion_buf_[kText], reinterpret_cast(pc), size); + // map_offset is the file offset of the first mapped page. + map_offset_ = (kELFHeaderSize + text_padding_)/kPageSize*kPageSize; + map_begin_ = Align(text_vma_ - kPageSize + 1, kPageSize); + map_end_ = Align(text_vma_ + size, kPageSize); +} + + +void ElfGen::AddCodeRegion(const char* name, uword pc, intptr_t size) { + MutexLocker ml(&lock_); + AddFunction(name, pc, size); + char end_name[256]; + OS::SNPrint(end_name, sizeof(end_name), "%s_end", name); + AddFunction(end_name, pc + size, 0); +} + + +int ElfGen::AddFunction(const char* name, uword pc, intptr_t size) { + ASSERT(text_vma_ != 0); // code must have been added + ByteArray* symtab = §ion_buf_[kSymtab]; + const int beg = symtab->size(); + WriteInt(symtab, AddName(name)); // st_name +#if defined(TARGET_ARCH_X64) + WriteShort(symtab, (kSTB_LOCAL << 4) + kSTT_FUNC); // st_info + (st_other<<8) + WriteShort(symtab, kText); // st_shndx +#endif + WriteWord(symtab, pc); // st_value + WriteWord(symtab, size); // st_size +#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) + // st_info + (st_other<<8) + WriteShort(symtab, (kSTB_EXPORTED << 4) + kSTT_FUNC); + WriteShort(symtab, kText); // st_shndx +#endif + ASSERT(symtab->size() - beg == kSymbolSize); + return beg / kSymbolSize; // symbol index in symtab +} + + +bool ElfGen::WriteToFile(File* handle) { + return WriteOutput(handle, WriteSectionToFile, PadFile); +} + + +bool ElfGen::WriteToMemory(ByteArray* region) { + return WriteOutput(region, WriteSectionToMemory, PadMemory); +} + + +int ElfGen::AddString(ByteArray* buf, const char* str) { + const int str_index = buf->size(); + WriteString(buf, str); + WriteByte(buf, 0); // terminating '\0' + return str_index; +} + + +int ElfGen::AddSectionName(const char* str) { + return AddString(§ion_buf_[kShStrtab], str); +} + + +int ElfGen::AddName(const char* str) { + return AddString(§ion_buf_[kStrtab], str); +} + + +void ElfGen::AddELFHeader(int shoff) { + ASSERT(text_vma_ != 0); // Code must have been added. + Write(&header_, kEI_MAG0_MAG3, 4); // EI_MAG0..EI_MAG3 +#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) + WriteByte(&header_, kELFCLASS32); // EI_CLASS +#elif defined(TARGET_ARCH_X64) + WriteByte(&header_, kELFCLASS64); // EI_CLASS +#endif + WriteByte(&header_, kELFDATA2LSB); // EI_DATA + WriteByte(&header_, kEV_CURRENT); // EI_VERSION + WriteByte(&header_, 0); // EI_PAD + WriteInt(&header_, 0); // EI_PAD + WriteInt(&header_, 0); // EI_PAD + WriteShort(&header_, kET_DYN); // e_type, fake a shared object. +#if defined(TARGET_ARCH_IA32) + WriteShort(&header_, kEM_386); // e_machine +#elif defined(TARGET_ARCH_X64) + WriteShort(&header_, kEM_X86_64); // e_machine +#elif defined(TARGET_ARCH_ARM) + WriteShort(&header_, kEM_ARM); // e_machine +#endif + WriteInt(&header_, kEV_CURRENT); // e_version + WriteWord(&header_, 0); // e_entry: none + WriteWord(&header_, 0); // e_phoff: no program header table. + WriteWord(&header_, shoff); // e_shoff: section header table offset. + WriteInt(&header_, 0); // e_flags: no flags. + WriteShort(&header_, kELFHeaderSize); // e_ehsize: header size. + WriteShort(&header_, kProgramHeaderEntrySize); // e_phentsize + WriteShort(&header_, 0); // e_phnum: no entries program header table. + WriteShort(&header_, kSectionHeaderEntrySize); // e_shentsize + // e_shnum: number of section header entries. + WriteShort(&header_, kNumSections); + WriteShort(&header_, kShStrtab); // e_shstrndx: index of shstrtab. + ASSERT(header_.size() == kELFHeaderSize); +} + + +void ElfGen::AddSectionHeader(int section, int offset) { + WriteInt(&sheaders_, section_name_[section]); + WriteInt(&sheaders_, section_attr[section].sh_type); + WriteWord(&sheaders_, section_attr[section].sh_flags); + // sh_addr: abs addr + WriteWord(&sheaders_, (section == kText) ? text_vma_ : 0); + WriteWord(&sheaders_, offset); // sh_offset: section file offset. + WriteWord(&sheaders_, section_buf_[section].size()); + WriteInt(&sheaders_, section_attr[section].sh_link); + WriteInt(&sheaders_, 0); + WriteWord(&sheaders_, section_attr[section].sh_addralign); + WriteWord(&sheaders_, section_attr[section].sh_entsize); + ASSERT(sheaders_.size() == kSectionHeaderEntrySize * (section + 1)); +} + + +// Pads the given section with zero bytes for the given aligment, assuming the +// section starts at given file offset; returns file offset after padded +// section. +int ElfGen::PadSection(ByteArray* section, int offset, int alignment) { + offset += section->size(); + int aligned_offset = Align(offset, alignment); + while (offset++ < aligned_offset) { + WriteByte(section, 0); // one byte padding. + } + return aligned_offset; +} + + +bool ElfGen::WriteOutput(void* handle, + OutputWriter writer, + OutputPadder padder) { + if (handle == NULL || writer == NULL || padder == NULL) { + return false; + } + + // Align all sections before writing the ELF header in order to calculate the + // file offset of the section header table, which is needed in the ELF header. + // Pad each section as required by the aligment constraint of the immediately + // following section, except the ELF header section, which requires special + // padding (text_padding_) to align the text_ section. + int offset = kELFHeaderSize + text_padding_; + for (int i = kText; i < kNumSections; i++) { + offset = PadSection(§ion_buf_[i], + offset, + section_attr[i+1].sh_addralign); + } + + const int shoff = offset; // Section header table offset. + + // Write elf header. + AddELFHeader(shoff); + offset = (*writer)(handle, header_); + + // Pad file before writing text section in order to align vma with file + // offset. + (*padder)(handle, text_padding_); + + offset += text_padding_; + ASSERT((text_vma_ - offset) % kPageSize == 0); + + // Section header at index 0 in section header table is always SHN_UNDEF: + for (int i = 0; i < kNumSections; i++) { + AddSectionHeader(i, offset); + offset += (*writer)(handle, section_buf_[i]); + } + // Write section header table. + ASSERT(offset == shoff); + offset += (*writer)(handle, sheaders_); + ASSERT(offset == shoff + kNumSections * kSectionHeaderEntrySize); + + return true; +} + + +DebugInfo::DebugInfo() { + handle_ = reinterpret_cast(new ElfGen()); + ASSERT(handle_ != NULL); +} + + +DebugInfo::~DebugInfo() { + ElfGen* elf_gen = reinterpret_cast(handle_); + delete elf_gen; +} + + +void DebugInfo::AddCode(uword pc, intptr_t size) { + ElfGen* elf_gen = reinterpret_cast(handle_); + elf_gen->AddCode(pc, size); +} + + +void DebugInfo::AddCodeRegion(const char* name, uword pc, intptr_t size) { + ElfGen* elf_gen = reinterpret_cast(handle_); + elf_gen->AddCodeRegion(name, pc, size); +} + + +bool DebugInfo::WriteToMemory(ByteArray* region) { + ElfGen* elf_gen = reinterpret_cast(handle_); + return elf_gen->WriteToMemory(region); +} + + +DebugInfo* DebugInfo::NewGenerator() { + return new DebugInfo(); +} + + +void DebugInfo::RegisterSection(const char* name, + uword entry_point, + intptr_t size) { + ElfGen* elf_section = new ElfGen(); + ASSERT(elf_section != NULL); + elf_section->AddCode(entry_point, size); + elf_section->AddCodeRegion(name, entry_point, size); + + ByteArray* dynamic_region = new ByteArray(); + ASSERT(dynamic_region != NULL); + + elf_section->WriteToMemory(dynamic_region); + + ::addDynamicSection(reinterpret_cast(dynamic_region->data()), + dynamic_region->size()); + dynamic_region->set_data(NULL); + delete dynamic_region; + delete elf_section; +} + + +void DebugInfo::UnregisterAllSections() { + ::deleteDynamicSections(); +} + +} // namespace dart diff --git a/runtime/vm/debuginfo_macos.cc b/runtime/vm/debuginfo_macos.cc new file mode 100644 index 00000000000..9fb941b4241 --- /dev/null +++ b/runtime/vm/debuginfo_macos.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/debuginfo.h" + +namespace dart { + +DebugInfo::DebugInfo() { + handle_ = NULL; +} + + +DebugInfo::~DebugInfo() { +} + + +void DebugInfo::AddCode(uword pc, intptr_t size) { + // Nothing to do as there is no support for this on macos. +} + + +void DebugInfo::AddCodeRegion(const char* name, uword pc, intptr_t size) { + // Nothing to do as there is no support for this on macos. +} + + +bool DebugInfo::WriteToMemory(ByteArray* region) { + // Nothing to do as there is no support for this on macos. + return false; +} + + +DebugInfo* DebugInfo::NewGenerator() { + return new DebugInfo(); +} + + +void DebugInfo::RegisterSection(const char* name, + uword entry_point, + intptr_t size) { + // Nothing to do as there is no support for this on macos. +} + + +void DebugInfo::UnregisterAllSections() { + // Nothing to do as there is no support for this on macos. +} + +} // namespace dart diff --git a/runtime/vm/debuginfo_win.cc b/runtime/vm/debuginfo_win.cc new file mode 100644 index 00000000000..9fb941b4241 --- /dev/null +++ b/runtime/vm/debuginfo_win.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/debuginfo.h" + +namespace dart { + +DebugInfo::DebugInfo() { + handle_ = NULL; +} + + +DebugInfo::~DebugInfo() { +} + + +void DebugInfo::AddCode(uword pc, intptr_t size) { + // Nothing to do as there is no support for this on macos. +} + + +void DebugInfo::AddCodeRegion(const char* name, uword pc, intptr_t size) { + // Nothing to do as there is no support for this on macos. +} + + +bool DebugInfo::WriteToMemory(ByteArray* region) { + // Nothing to do as there is no support for this on macos. + return false; +} + + +DebugInfo* DebugInfo::NewGenerator() { + return new DebugInfo(); +} + + +void DebugInfo::RegisterSection(const char* name, + uword entry_point, + intptr_t size) { + // Nothing to do as there is no support for this on macos. +} + + +void DebugInfo::UnregisterAllSections() { + // Nothing to do as there is no support for this on macos. +} + +} // namespace dart diff --git a/runtime/vm/disassembler.cc b/runtime/vm/disassembler.cc new file mode 100644 index 00000000000..0247dc96a42 --- /dev/null +++ b/runtime/vm/disassembler.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/disassembler.h" + +#include "vm/assembler.h" +#include "vm/globals.h" +#include "vm/os.h" + +namespace dart { + +void DisassembleToStdout::ConsumeInstruction(char* hex_buffer, + intptr_t hex_size, + char* human_buffer, + intptr_t human_size, + uword pc) { + static const int kHexColumnWidth = 18; + uint8_t* pc_ptr = reinterpret_cast(pc); + OS::Print("%p %s", pc_ptr, hex_buffer); + int hex_length = strlen(hex_buffer); + if (hex_length < kHexColumnWidth) { + for (int i = kHexColumnWidth - hex_length; i > 0; i--) { + OS::Print(" "); + } + } + OS::Print("%s", human_buffer); + OS::Print("\n"); +} + + +void Disassembler::Disassemble(uword start, + uword end, + DisassemblyFormatter* formatter) { + ASSERT(formatter != NULL); + char hex_buffer[kHexadecimalBufferSize]; // Instruction in hexadecimal form. + char human_buffer[kUserReadableBufferSize]; // Human-readable instruction. + uword pc = start; + while (pc < end) { + int instruction_length = DecodeInstruction(hex_buffer, + sizeof(hex_buffer), + human_buffer, + sizeof(human_buffer), + pc); + formatter->ConsumeInstruction(hex_buffer, + sizeof(hex_buffer), + human_buffer, + sizeof(human_buffer), + pc); + pc += instruction_length; + } +} + + +void Disassembler::DisassembleMemoryRegionRange( + const MemoryRegion& instructions, + uword from, + uword to, + DisassemblyFormatter *formatter) { + ASSERT(formatter != NULL); + char hex_buffer[kHexadecimalBufferSize]; // Instruction in hexadecimal form. + char human_buffer[kUserReadableBufferSize]; // Human-readable instruction. + uword start = instructions.start(); + uword end = instructions.end(); + ASSERT((from >= start) && (to <= end) && (from <= to)); + uword pc = start; + uword stop = Utils::Minimum(end, to); + while (pc < stop) { + int instruction_length = DecodeInstruction(hex_buffer, + sizeof(hex_buffer), + human_buffer, + sizeof(human_buffer), + pc); + if (pc >= from) { + formatter->ConsumeInstruction(hex_buffer, + sizeof(hex_buffer), + human_buffer, + sizeof(human_buffer), + pc); + } + pc += instruction_length; + } +} + +} // namespace dart diff --git a/runtime/vm/disassembler.h b/runtime/vm/disassembler.h new file mode 100644 index 00000000000..87eaec4cba3 --- /dev/null +++ b/runtime/vm/disassembler.h @@ -0,0 +1,101 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_DISASSEMBLER_H_ +#define VM_DISASSEMBLER_H_ + +#include "vm/allocation.h" +#include "vm/assembler.h" +#include "vm/globals.h" + +namespace dart { + +// Froward declaration. +class MemoryRegion; + +// Disassembly formatter interface, which consumes the +// disassembled instructions in any desired form. +class DisassemblyFormatter { + public: + DisassemblyFormatter() { } + virtual ~DisassemblyFormatter() { } + + // Consume the decoded instruction at the given pc. + virtual void ConsumeInstruction(char* hex_buffer, + intptr_t hex_size, + char* human_buffer, + intptr_t human_size, + uword pc) = 0; +}; + + +// Basic disassembly formatter that outputs the disassembled instruction +// to stdout. +class DisassembleToStdout : public DisassemblyFormatter { + public: + DisassembleToStdout() : DisassemblyFormatter() { } + ~DisassembleToStdout() { } + + virtual void ConsumeInstruction(char* hex_buffer, + intptr_t hex_size, + char* human_buffer, + intptr_t human_size, + uword pc); + + private: + DISALLOW_ALLOCATION() + DISALLOW_COPY_AND_ASSIGN(DisassembleToStdout); +}; + + +// Disassemble instructions. +class Disassembler : public AllStatic { + public: + // Disassemble instructions between start and end. + // (The assumption is that start is at a valid instruction). + static void Disassemble(uword start, + uword end, + DisassemblyFormatter* formatter); + static void Disassemble(uword start, uword end) { + DisassembleToStdout stdout_formatter; + Disassemble(start, end, &stdout_formatter); + } + + // Disassemble instructions in a memory region. + static void DisassembleMemoryRegionRange(const MemoryRegion& instructions, + uword from, + uword to, + DisassemblyFormatter* formatter); + static void DisassembleMemoryRegion(const MemoryRegion& instructions, + DisassemblyFormatter* formatter) { + uword start = instructions.start(); + uword end = instructions.end(); + Disassemble(start, end, formatter); + } + static void DisassembleMemoryRegion(const MemoryRegion& instructions) { + uword start = instructions.start(); + uword end = instructions.end(); + Disassemble(start, end); + } + + // Decodes one instruction. + // Writes a hexadecimal representation into the hex_buffer and a + // human-readable representation into the human_buffer. + // Returns the length of the decoded instruction in bytes. + static int DecodeInstruction(char* hex_buffer, intptr_t hex_size, + char* human_buffer, intptr_t human_size, + uword pc); + + // Returns the name for the given register. For instance the Register EAX + // returns the string "eax". + static const char* RegisterName(Register reg); + + private: + static const int kHexadecimalBufferSize = 32; + static const int kUserReadableBufferSize = 256; +}; + +} // namespace dart + +#endif // VM_DISASSEMBLER_H_ diff --git a/runtime/vm/disassembler_arm.cc b/runtime/vm/disassembler_arm.cc new file mode 100644 index 00000000000..5fbacb96ea8 --- /dev/null +++ b/runtime/vm/disassembler_arm.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM. +#if defined(TARGET_ARCH_ARM) + +#include "vm/disassembler.h" + +#include "vm/assert.h" + +namespace dart { + +int Disassembler::DecodeInstruction(char* hex_buffer, intptr_t hex_size, + char* human_buffer, intptr_t human_size, + uword pc) { + UNIMPLEMENTED(); + return 0; +} + + +const char* Disassembler::RegisterName(Register reg) { + UNIMPLEMENTED(); + return NULL; +} + +} // namespace dart + +#endif // defined TARGET_ARCH_ARM diff --git a/runtime/vm/disassembler_ia32.cc b/runtime/vm/disassembler_ia32.cc new file mode 100644 index 00000000000..1b7bf503278 --- /dev/null +++ b/runtime/vm/disassembler_ia32.cc @@ -0,0 +1,1514 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. +#if defined(TARGET_ARCH_IA32) + +#include "vm/disassembler.h" + +#include "vm/allocation.h" +#include "vm/code_index_table.h" +#include "vm/heap.h" +#include "vm/os.h" +#include "vm/stub_code.h" +#include "vm/utils.h" + +namespace dart { + +// Tables used for decoding of x86 instructions. +enum OperandOrder { + UNSET_OP_ORDER = 0, + REG_OPER_OP_ORDER, + OPER_REG_OP_ORDER +}; + + +struct ByteMnemonic { + int b; // -1 terminates, otherwise must be in range (0..255) + const char* mnem; + OperandOrder op_order_; +}; + + +static ByteMnemonic two_operands_instr[] = { + {0x01, "add", OPER_REG_OP_ORDER}, + {0x03, "add", REG_OPER_OP_ORDER}, + {0x09, "or", OPER_REG_OP_ORDER}, + {0x0B, "or", REG_OPER_OP_ORDER}, + {0x13, "adc", REG_OPER_OP_ORDER}, + {0x1B, "sbb", REG_OPER_OP_ORDER}, + {0x21, "and", OPER_REG_OP_ORDER}, + {0x23, "and", REG_OPER_OP_ORDER}, + {0x29, "sub", OPER_REG_OP_ORDER}, + {0x2B, "sub", REG_OPER_OP_ORDER}, + {0x31, "xor", OPER_REG_OP_ORDER}, + {0x33, "xor", REG_OPER_OP_ORDER}, + {0x39, "cmp", OPER_REG_OP_ORDER}, + {0x3B, "cmp", REG_OPER_OP_ORDER}, + {0x85, "test", REG_OPER_OP_ORDER}, + {0x87, "xchg", REG_OPER_OP_ORDER}, + {0x8A, "mov_b", REG_OPER_OP_ORDER}, + {0x8B, "mov", REG_OPER_OP_ORDER}, + {0x8D, "lea", REG_OPER_OP_ORDER}, + {-1, "", UNSET_OP_ORDER} +}; + + +static ByteMnemonic zero_operands_instr[] = { + {0xC3, "ret", UNSET_OP_ORDER}, + {0xC9, "leave", UNSET_OP_ORDER}, + {0x90, "nop", UNSET_OP_ORDER}, + {0xF4, "hlt", UNSET_OP_ORDER}, + {0xCC, "int3", UNSET_OP_ORDER}, + {0x60, "pushad", UNSET_OP_ORDER}, + {0x61, "popad", UNSET_OP_ORDER}, + {0x9C, "pushfd", UNSET_OP_ORDER}, + {0x9D, "popfd", UNSET_OP_ORDER}, + {0x9E, "sahf", UNSET_OP_ORDER}, + {0x99, "cdq", UNSET_OP_ORDER}, + {0x9B, "fwait", UNSET_OP_ORDER}, + {-1, "", UNSET_OP_ORDER} +}; + + +static ByteMnemonic call_jump_instr[] = { + {0xE8, "call", UNSET_OP_ORDER}, + {0xE9, "jmp", UNSET_OP_ORDER}, + {-1, "", UNSET_OP_ORDER} +}; + + +static ByteMnemonic short_immediate_instr[] = { + {0x05, "add", UNSET_OP_ORDER}, + {0x0D, "or", UNSET_OP_ORDER}, + {0x15, "adc", UNSET_OP_ORDER}, + {0x25, "and", UNSET_OP_ORDER}, + {0x2D, "sub", UNSET_OP_ORDER}, + {0x35, "xor", UNSET_OP_ORDER}, + {0x3D, "cmp", UNSET_OP_ORDER}, + {-1, "", UNSET_OP_ORDER} +}; + + +static const char* jump_conditional_mnem[] = { + /*0*/ "jo", "jno", "jc", "jnc", + /*4*/ "jz", "jnz", "jna", "ja", + /*8*/ "js", "jns", "jpe", "jpo", + /*12*/ "jl", "jnl", "jng", "jg" +}; + + +static const char* set_conditional_mnem[] = { + /*0*/ "seto", "setno", "setc", "setnc", + /*4*/ "setz", "setnz", "setna", "seta", + /*8*/ "sets", "setns", "setpe", "setpo", + /*12*/ "setl", "setnl", "setng", "setg" +}; + + +static const char* conditional_move_mnem[] = { + /*0*/ "cmovo", "cmovno", "cmovc", "cmovnc", + /*4*/ "cmovz", "cmovnz", "cmovna", "cmova", + /*8*/ "cmovs", "cmovns", "cmovpe", "cmovpo", + /*12*/ "cmovl", "cmovnl", "cmovng", "cmovg" +}; + + +enum InstructionType { + NO_INSTR, + ZERO_OPERANDS_INSTR, + TWO_OPERANDS_INSTR, + JUMP_CONDITIONAL_SHORT_INSTR, + REGISTER_INSTR, + MOVE_REG_INSTR, + CALL_JUMP_INSTR, + SHORT_IMMEDIATE_INSTR +}; + + +struct InstructionDesc { + const char* mnem; + InstructionType type; + OperandOrder op_order_; +}; + + +class InstructionTable : public ValueObject { + public: + InstructionTable(); + const InstructionDesc& Get(uint8_t x) const { return instructions_[x]; } + + private: + InstructionDesc instructions_[256]; + void Clear(); + void Init(); + void CopyTable(ByteMnemonic bm[], InstructionType type); + void SetTableRange(InstructionType type, + uint8_t start, + uint8_t end, + const char* mnem); + void AddJumpConditionalShort(); + + DISALLOW_COPY_AND_ASSIGN(InstructionTable); +}; + + +InstructionTable::InstructionTable() { + Clear(); + Init(); +} + + +void InstructionTable::Clear() { + for (int i = 0; i < 256; i++) { + instructions_[i].mnem = ""; + instructions_[i].type = NO_INSTR; + instructions_[i].op_order_ = UNSET_OP_ORDER; + } +} + + +void InstructionTable::Init() { + CopyTable(two_operands_instr, TWO_OPERANDS_INSTR); + CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR); + CopyTable(call_jump_instr, CALL_JUMP_INSTR); + CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR); + AddJumpConditionalShort(); + SetTableRange(REGISTER_INSTR, 0x40, 0x47, "inc"); + SetTableRange(REGISTER_INSTR, 0x48, 0x4F, "dec"); + SetTableRange(REGISTER_INSTR, 0x50, 0x57, "push"); + SetTableRange(REGISTER_INSTR, 0x58, 0x5F, "pop"); + SetTableRange(REGISTER_INSTR, 0x91, 0x97, "xchg eax,"); // 0x90 is nop. + SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "mov"); +} + + +void InstructionTable::CopyTable(ByteMnemonic bm[], InstructionType type) { + for (int i = 0; bm[i].b >= 0; i++) { + InstructionDesc* id = &instructions_[bm[i].b]; + id->mnem = bm[i].mnem; + id->op_order_ = bm[i].op_order_; + ASSERT(id->type == NO_INSTR); // Information already entered + id->type = type; + } +} + + +void InstructionTable::SetTableRange(InstructionType type, + uint8_t start, + uint8_t end, + const char* mnem) { + for (uint8_t b = start; b <= end; b++) { + InstructionDesc* id = &instructions_[b]; + ASSERT(id->type == NO_INSTR); // Information already entered + id->mnem = mnem; + id->type = type; + } +} + + +void InstructionTable::AddJumpConditionalShort() { + for (uint8_t b = 0x70; b <= 0x7F; b++) { + InstructionDesc* id = &instructions_[b]; + ASSERT(id->type == NO_INSTR); // Information already entered + id->mnem = jump_conditional_mnem[b & 0x0F]; + id->type = JUMP_CONDITIONAL_SHORT_INSTR; + } +} + + +static InstructionTable instruction_table; + + +// Mnemonics for instructions 0xF0 byte. +// Returns NULL if the instruction is not handled here. +static const char* F0Mnem(uint8_t f0byte) { + switch (f0byte) { + case 0xA2: return "cpuid"; + case 0x31: return "rdtsc"; + case 0xBE: return "movsx_b"; + case 0xBF: return "movsx_w"; + case 0xB6: return "movzx_b"; + case 0xB7: return "movzx_w"; + case 0xAF: return "imul"; + case 0xA5: return "shld"; + case 0xAD: return "shrd"; + case 0xAB: return "bts"; + case 0xB1: return "cmpxchg"; + case 0x57: return "xorps"; + default: return NULL; + } +} + + +// The implementation of x86 decoding based on the above tables. +class X86Decoder : public ValueObject { + public: + X86Decoder(char* buffer, intptr_t buffer_size) + : buffer_(buffer), + buffer_size_(buffer_size), + buffer_pos_(0) { + buffer_[buffer_pos_] = '\0'; + } + + ~X86Decoder() {} + + // Writes one disassembled instruction into the buffer (0-terminated). + // Returns the length of the disassembled machine instruction in bytes. + int InstructionDecode(uword pc); + + private: + enum { + eax = 0, + ecx = 1, + edx = 2, + ebx = 3, + esp = 4, + ebp = 5, + esi = 6, + edi = 7 + }; + + // Bottleneck functions to print into the out_buffer. + void PrintInt(int value); + void PrintHex(int value); + void Print(const char* str); + + // Printing of common values. + void PrintCPURegister(int reg); + void PrintCPUByteRegister(int reg); + void PrintXmmRegister(int reg); + void PrintAddress(uword addr); + + typedef void (X86Decoder::*RegisterNamePrinter)(int reg); + + int PrintRightOperandHelper(uint8_t* modrmp, + RegisterNamePrinter register_printer); + int PrintRightOperand(uint8_t* modrmp); + int PrintRightXmmOperand(uint8_t* modrmp); + int PrintRightByteOperand(uint8_t* modrmp); + int PrintOperands(const char* mnem, OperandOrder op_order, uint8_t* data); + int PrintImmediateOp(uint8_t* data); + + // Handle special encodings. + int JumpShort(uint8_t* data); + int JumpConditional(uint8_t* data, const char* comment); + int JumpConditionalShort(uint8_t* data, const char* comment); + int SetCC(uint8_t* data); + int CMov(uint8_t* data); + int D1D3C1Instruction(uint8_t* data); + int F7Instruction(uint8_t* data); + int FPUInstruction(uint8_t* data); + int DecodeEnter(uint8_t* data); + void CheckPrintStop(uint8_t* data); + + // Disassembler helper functions. + static void GetModRm(uint8_t data, int* mod, int* regop, int* rm) { + *mod = (data >> 6) & 3; + *regop = (data & 0x38) >> 3; + *rm = data & 7; + } + + static void GetSib(uint8_t data, int* scale, int* index, int* base) { + *scale = (data >> 6) & 3; + *index = (data >> 3) & 7; + *base = data & 7; + } + + + // Convenience functions. + char* get_buffer() const { return buffer_; } + char* current_position_in_buffer() { return buffer_ + buffer_pos_; } + intptr_t remaining_size_in_buffer() { return buffer_size_ - buffer_pos_; } + + char* buffer_; // Decode instructions into this buffer. + intptr_t buffer_size_; // The size of the buffer_. + intptr_t buffer_pos_; // Current character position in the buffer_. + + DISALLOW_COPY_AND_ASSIGN(X86Decoder); +}; + + +void X86Decoder::PrintInt(int value) { + char int_buffer[16]; + OS::SNPrint(int_buffer, sizeof(int_buffer), "0x%x", value); + Print(int_buffer); +} + + +// Append the int value (printed in hex) to the output buffer. +void X86Decoder::PrintHex(int value) { + char hex_buffer[16]; + OS::SNPrint(hex_buffer, sizeof(hex_buffer), "0x%x", value); + Print(hex_buffer); +} + + +// Append the str to the output buffer. +void X86Decoder::Print(const char* str) { + char cur = *str++; + while (cur != '\0' && (buffer_pos_ < (buffer_size_ - 1))) { + buffer_[buffer_pos_++] = cur; + cur = *str++; + } + buffer_[buffer_pos_] = '\0'; +} + + +static const int kMaxCPURegisters = 8; +static const char* cpu_regs[kMaxCPURegisters] = { + "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" +}; + +static const int kMaxXmmRegisters = 8; +static const char* xmm_regs[kMaxXmmRegisters] = { + "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7" +}; + + +void X86Decoder::PrintCPURegister(int reg) { + ASSERT(0 <= reg); + ASSERT(reg < kMaxCPURegisters); + Print(cpu_regs[reg]); +} + + +void X86Decoder::PrintCPUByteRegister(int reg) { + UNIMPLEMENTED(); +} + + +void X86Decoder::PrintXmmRegister(int reg) { + ASSERT(0 <= reg); + ASSERT(reg < kMaxXmmRegisters); + Print(xmm_regs[reg]); +} + + +void X86Decoder::PrintAddress(uword addr) { + NoGCScope no_gc; + char addr_buffer[32]; + OS::SNPrint(addr_buffer, sizeof(addr_buffer), "%p", addr); + Print(addr_buffer); + // Try to print as heap object or stub name + if (!Isolate::Current()->heap()->CodeContains(addr) && + Isolate::Current()->heap()->Contains(addr - kHeapObjectTag)) { + Print(" '"); + const Object& obj = Object::Handle(reinterpret_cast(addr)); + Print(obj.ToCString()); + Print("'"); + } else { + // 'addr' is not an object, but probably a code address. + const char* name_of_stub = StubCode::NameOfStub(addr); + if (name_of_stub != NULL) { + Print(" [stub: "); + Print(name_of_stub); + Print("]"); + } else { + CodeIndexTable* code_index_table = Isolate::Current()->code_index_table(); + if (code_index_table != NULL) { + // Print only if jumping to entry point. + const Function& function = Function::Handle( + code_index_table->LookupFunction(addr)); + if (!function.IsNull() && + (Code::Handle(function.code()).EntryPoint() == addr)) { + const char* name_of_function = function.ToFullyQualifiedCString(); + Print(" ["); + Print(name_of_function); + Print("]"); + } + } + } + } +} + + +int X86Decoder::PrintRightOperandHelper(uint8_t* modrmp, + RegisterNamePrinter register_printer) { + int mod, regop, rm; + GetModRm(*modrmp, &mod, ®op, &rm); + switch (mod) { + case 0: + if (rm == ebp) { + int32_t disp = *reinterpret_cast(modrmp+1); + Print("["); + PrintHex(disp); + Print("]"); + return 5; + } else if (rm == esp) { + uint8_t sib = *(modrmp + 1); + int scale, index, base; + GetSib(sib, &scale, &index, &base); + if (index == esp && base == esp && scale == 0 /*times_1*/) { + Print("["); + PrintCPURegister(rm); + Print("]"); + return 2; + } else if (base == ebp) { + int32_t disp = *reinterpret_cast(modrmp + 2); + Print("["); + PrintCPURegister(index); + Print("*"); + PrintInt(1 << scale); + if (disp < 0) { + Print("-"); + disp = -disp; + } else { + Print("+"); + } + PrintHex(disp); + Print("]"); + return 6; + } else if (index != esp && base != ebp) { + // [base+index*scale] + Print("["); + PrintCPURegister(base); + Print("+"); + PrintCPURegister(index); + Print("*"); + PrintInt(1 << scale); + Print("]"); + return 2; + } else { + UNIMPLEMENTED(); + return 1; + } + } else { + Print("["); + PrintCPURegister(rm); + Print("]"); + return 1; + } + break; + case 1: // fall through + case 2: + if (rm == esp) { + uint8_t sib = *(modrmp + 1); + int scale, index, base; + GetSib(sib, &scale, &index, &base); + int disp = (mod == 2) ? + *reinterpret_cast(modrmp + 2) : + *reinterpret_cast(modrmp + 2); + if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) { + Print("["); + PrintCPURegister(rm); + if (disp < 0) { + Print("-"); + disp = -disp; + } else { + Print("+"); + } + PrintHex(disp); + Print("]"); + } else { + Print("["); + PrintCPURegister(base); + Print("+"); + PrintCPURegister(index); + Print("*"); + PrintInt(1 << scale); + if (disp < 0) { + Print("-"); + disp = -disp; + } else { + Print("+"); + } + PrintHex(disp); + Print("]"); + } + return mod == 2 ? 6 : 3; + } else { + // No sib. + int disp = (mod == 2) ? + *reinterpret_cast(modrmp + 1) : + *reinterpret_cast(modrmp + 1); + Print("["); + PrintCPURegister(rm); + if (disp < 0) { + Print("-"); + disp = -disp; + } else { + Print("+"); + } + PrintHex(disp); + Print("]"); + return mod == 2 ? 5 : 2; + } + break; + case 3: + (this->*register_printer)(rm); + return 1; + default: + UNIMPLEMENTED(); + return 1; + } + UNREACHABLE(); +} + + +int X86Decoder::PrintRightOperand(uint8_t* modrmp) { + return PrintRightOperandHelper(modrmp, &X86Decoder::PrintCPURegister); +} + + +int X86Decoder::PrintRightXmmOperand(uint8_t* modrmp) { + return PrintRightOperandHelper(modrmp, &X86Decoder::PrintXmmRegister); +} + + +int X86Decoder::PrintRightByteOperand(uint8_t* modrmp) { + UNIMPLEMENTED(); + return 0; +} + + +int X86Decoder::PrintOperands(const char* mnem, + OperandOrder op_order, + uint8_t* data) { + uint8_t modrm = *data; + int mod, regop, rm; + GetModRm(modrm, &mod, ®op, &rm); + int advance = 0; + switch (op_order) { + case REG_OPER_OP_ORDER: { + Print(mnem); + Print(" "); + PrintCPURegister(regop); + Print(","); + advance = PrintRightOperand(data); + break; + } + case OPER_REG_OP_ORDER: { + Print(mnem); + Print(" "); + advance = PrintRightOperand(data); + Print(","); + PrintCPURegister(regop); + break; + } + default: + UNREACHABLE(); + break; + } + return advance; +} + + +int X86Decoder::PrintImmediateOp(uint8_t* data) { + bool sign_extension_bit = (*data & 0x02) != 0; + uint8_t modrm = *(data+1); + int mod, regop, rm; + GetModRm(modrm, &mod, ®op, &rm); + const char* mnem = "Imm???"; + switch (regop) { + case 0: mnem = "add"; break; + case 1: mnem = "or"; break; + case 2: mnem = "adc"; break; + case 3: mnem = "sbb"; break; + case 4: mnem = "and"; break; + case 5: mnem = "sub"; break; + case 6: mnem = "xor"; break; + case 7: mnem = "cmp"; break; + default: UNIMPLEMENTED(); + } + Print(mnem); + Print(" "); + int count = PrintRightOperand(data+1); + if (sign_extension_bit) { + Print(","); + PrintHex(*(data + 1 + count)); + return 1 + count + 1 /*int8*/; + } else { + Print(","); + PrintHex(*reinterpret_cast(data + 1 + count)); + return 1 + count + 4 /*int32_t*/; + } +} + + +int X86Decoder::DecodeEnter(uint8_t* data) { + uint16_t size = *reinterpret_cast(data + 1); + uint8_t level = *reinterpret_cast(data + 3); + Print("enter "); + PrintInt(size); + Print(", "); + PrintInt(level); + return 4; +} + + +// Returns number of bytes used, including *data. +int X86Decoder::JumpShort(uint8_t* data) { + ASSERT(*data == 0xEB); + uint8_t b = *(data+1); + uword dest = reinterpret_cast(data) + static_cast(b) + 2; + Print("jmp "); + PrintAddress(dest); + return 2; +} + + +// Returns number of bytes used, including *data. +int X86Decoder::JumpConditional(uint8_t* data, const char* comment) { + ASSERT(*data == 0x0F); + uint8_t cond = *(data+1) & 0x0F; + uword dest = reinterpret_cast(data) + + *reinterpret_cast(data+2) + 6; + const char* mnem = jump_conditional_mnem[cond]; + Print(mnem); + Print(" "); + PrintAddress(dest); + if (comment != NULL) { + Print(", "); + Print(comment); + } + return 6; // includes 0x0F +} + + +// Returns number of bytes used, including *data. +int X86Decoder::JumpConditionalShort(uint8_t* data, const char* comment) { + uint8_t cond = *data & 0x0F; + uint8_t b = *(data+1); + word dest = reinterpret_cast(data) + static_cast(b) + 2; + const char* mnem = jump_conditional_mnem[cond]; + Print(mnem); + Print(" "); + PrintAddress(dest); + if (comment != NULL) { + Print(", "); + Print(comment); + } + return 2; +} + + +// Returns number of bytes used, including *data. +int X86Decoder::SetCC(uint8_t* data) { + ASSERT(*data == 0x0F); + uint8_t cond = *(data+1) & 0x0F; + const char* mnem = set_conditional_mnem[cond]; + Print(mnem); + Print(" "); + PrintRightByteOperand(data+2); + return 3; // includes 0x0F +} + + +// Returns number of bytes used, including *data. +int X86Decoder::CMov(uint8_t* data) { + ASSERT(*data == 0x0F); + uint8_t cond = *(data + 1) & 0x0F; + const char* mnem = conditional_move_mnem[cond]; + int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2); + return 2 + op_size; // includes 0x0F +} + + +int X86Decoder::D1D3C1Instruction(uint8_t* data) { + uint8_t op = *data; + ASSERT(op == 0xD1 || op == 0xD3 || op == 0xC1); + uint8_t modrm = *(data+1); + int mod, regop, rm; + GetModRm(modrm, &mod, ®op, &rm); + int imm8 = -1; + int num_bytes = 2; + if (mod == 3) { + const char* mnem = NULL; + if (op == 0xD1) { + imm8 = 1; + switch (regop) { + case edx: mnem = "rcl"; break; + case edi: mnem = "sar"; break; + case esp: mnem = "shl"; break; + case ebp: mnem = "shr"; break; + default: UNIMPLEMENTED(); + } + } else if (op == 0xC1) { + imm8 = *(data+2); + num_bytes = 3; + switch (regop) { + case edx: mnem = "rcl"; break; + case esp: mnem = "shl"; break; + case ebp: mnem = "shr"; break; + case edi: mnem = "sar"; break; + default: UNIMPLEMENTED(); + } + } else if (op == 0xD3) { + switch (regop) { + case esp: mnem = "shl"; break; + case ebp: mnem = "shr"; break; + case edi: mnem = "sar"; break; + default: UNIMPLEMENTED(); + } + } + ASSERT(mnem != NULL); + Print(mnem); + Print(" "); + PrintCPURegister(rm); + Print(","); + if (imm8 > 0) { + PrintInt(imm8); + } else { + Print("cl"); + } + } else { + UNIMPLEMENTED(); + } + return num_bytes; +} + + +// Returns number of bytes used, including *data. +int X86Decoder::F7Instruction(uint8_t* data) { + ASSERT(*data == 0xF7); + uint8_t modrm = *(data+1); + int mod, regop, rm; + GetModRm(modrm, &mod, ®op, &rm); + if (mod == 3 && regop != 0) { + const char* mnem = NULL; + switch (regop) { + case 2: mnem = "not"; break; + case 3: mnem = "neg"; break; + case 4: mnem = "mul"; break; + case 5: mnem = "imul"; break; + case 7: mnem = "idiv"; break; + default: UNIMPLEMENTED(); + } + Print(mnem); + Print(" "); + PrintCPURegister(rm); + return 2; + } else if (mod == 3 && regop == eax) { + int32_t imm = *reinterpret_cast(data+2); + Print("test "); + PrintCPURegister(rm); + Print(","); + PrintHex(imm); + return 6; + } else if (regop == eax) { + Print("test "); + int count = PrintRightOperand(data+1); + int32_t imm = *reinterpret_cast(data+1+count); + Print(","); + PrintHex(imm); + return 1+count+4 /*int32_t*/; + } else if (regop == 5) { + Print("imul "); + int count = PrintRightOperand(data + 1); + return 1 + count; + } else if (regop == 4) { + Print("mul "); + int count = PrintRightOperand(data + 1); + return 1 + count; + } else { + OS::Print("F7 Instr regop %d\n", regop); + UNIMPLEMENTED(); + return 2; + } +} + +// Returns number of bytes used, including *data. +int X86Decoder::FPUInstruction(uint8_t* data) { + uint8_t b1 = *data; + uint8_t b2 = *(data + 1); + if (b1 == 0xD9) { + const char* mnem = NULL; + switch (b2) { + case 0xE0: mnem = "fchs"; break; + case 0xE1: mnem = "fabs"; break; + case 0xE4: mnem = "ftst"; break; + case 0xE8: mnem = "fld1"; break; + case 0xEE: mnem = "fldz"; break; + case 0xF2: mnem = "fptan"; break; + case 0xF5: mnem = "fprem1"; break; + case 0xF8: mnem = "fprem"; break; + case 0xF7: mnem = "fincstp"; break; + case 0xFE: mnem = "fsin"; break; + case 0xFF: mnem = "fcos"; break; + } + if (mnem != NULL) { + Print(mnem); + return 2; + } else if ((b2 & 0xF8) == 0xC8) { + Print("fxch st"); + PrintInt(b2 & 0x7); + return 2; + } else { + int mod, regop, rm; + GetModRm(*(data+1), &mod, ®op, &rm); + const char* mnem = "? FPU 0xD9"; + switch (regop) { + case 0: mnem = "fld_s"; break; + case 3: mnem = "fstp_s"; break; + case 5: mnem = "fldcw"; break; + case 7: mnem = "fnstcw"; break; + default: UNIMPLEMENTED(); + } + Print(mnem); + Print(" "); + int count = PrintRightOperand(data + 1); + return count + 1; + } + } else if (b1 == 0xDD) { + if ((b2 & 0xF8) == 0xC0) { + Print("ffree st"); + PrintInt(b2 & 0x7); + return 2; + } else { + int mod, regop, rm; + GetModRm(*(data+1), &mod, ®op, &rm); + const char* mnem = "? FPU 0xDD"; + switch (regop) { + case 0: mnem = "fld_d"; break; + case 3: mnem = "fstp_d"; break; + default: UNIMPLEMENTED(); + } + Print(mnem); + Print(" "); + int count = PrintRightOperand(data + 1); + return count + 1; + } + } else if (b1 == 0xDB) { + int mod, regop, rm; + GetModRm(*(data+1), &mod, ®op, &rm); + const char* mnem = "? FPU 0xDB"; + switch (regop) { + case 0: mnem = "fild_s"; break; + case 2: mnem = "fist_s"; break; + case 3: mnem = "fistp_s"; break; + default: UNIMPLEMENTED(); + } + Print(mnem); + Print(" "); + int count = PrintRightOperand(data + 1); + return count + 1; + } else if (b1 == 0xDF) { + if (b2 == 0xE0) { + Print("fnstsw_ax"); + return 2; + } + int mod, regop, rm; + GetModRm(*(data+1), &mod, ®op, &rm); + const char* mnem = "? FPU 0xDF"; + switch (regop) { + case 5: mnem = "fild_d"; break; + case 7: mnem = "fistp_d"; break; + default: UNIMPLEMENTED(); + } + Print(mnem); + Print(" "); + int count = PrintRightOperand(data + 1); + return count + 1; + } else if (b1 == 0xDC || b1 == 0xDE) { + bool is_pop = (b1 == 0xDE); + if (is_pop && b2 == 0xD9) { + Print("fcompp"); + return 2; + } + const char* mnem = "FP0xDC"; + switch (b2 & 0xF8) { + case 0xC0: mnem = "fadd"; break; + case 0xE8: mnem = "fsub"; break; + case 0xC8: mnem = "fmul"; break; + case 0xF8: mnem = "fdiv"; break; + default: UNIMPLEMENTED(); + } + Print(mnem); + Print(is_pop ? "p" : ""); + Print(" st"); + PrintInt(b2 & 0x7); + return 2; + } else if (b1 == 0xDA && b2 == 0xE9) { + const char* mnem = "fucompp"; + Print(mnem); + return 2; + } + Print("Unknown FP instruction"); + return 2; +} + + +// Called when disassembling test eax, 0xXXXXX. +void X86Decoder::CheckPrintStop(uint8_t* data) { + // Recognize stop pattern. + if (*reinterpret_cast(data + 5) == 0xCC) { + Print(" STOP:'"); + const char* text = *reinterpret_cast(data + 1); + Print(text); + Print("'"); + } +} + + +int X86Decoder::InstructionDecode(uword pc) { + uint8_t* data = reinterpret_cast(pc); + // Check for hints. + const char* branch_hint = NULL; + // We use these two prefixes only with branch prediction + switch (*data) { + case 0x3E: // ds + branch_hint = "predicted taken"; + data++; + break; + case 0x2E: // cs + branch_hint = "predicted not taken"; + data++; + break; + case 0xF0: // lock + Print("lock "); + data++; + break; + default: // Ignore all other instructions. + break; + } + bool processed = true; // Will be set to false if the current instruction + // is not in 'instructions' table. + const InstructionDesc& idesc = instruction_table.Get(*data); + switch (idesc.type) { + case ZERO_OPERANDS_INSTR: + Print(idesc.mnem); + data++; + break; + + case TWO_OPERANDS_INSTR: + data++; + data += PrintOperands(idesc.mnem, idesc.op_order_, data); + break; + + case JUMP_CONDITIONAL_SHORT_INSTR: + data += JumpConditionalShort(data, branch_hint); + break; + + case REGISTER_INSTR: + Print(idesc.mnem); + Print(" "); + PrintCPURegister(*data & 0x07); + data++; + break; + + case MOVE_REG_INSTR: { + uword addr = *reinterpret_cast(data+1); + Print("mov "); + PrintCPURegister(*data & 0x07), + Print(","); + PrintAddress(addr); + data += 5; + break; + } + + case CALL_JUMP_INSTR: { + uword addr = reinterpret_cast(data) + + *reinterpret_cast(data+1) + 5; + Print(idesc.mnem); + Print(" "); + PrintAddress(addr); + data += 5; + break; + } + + case SHORT_IMMEDIATE_INSTR: { + uword addr = *reinterpret_cast(data+1); + Print(idesc.mnem); + Print(" eax, "); + PrintAddress(addr); + data += 5; + break; + } + + case NO_INSTR: + processed = false; + break; + + default: + UNIMPLEMENTED(); // This type is not implemented. + } + //---------------------------- + if (!processed) { + switch (*data) { + case 0xC2: + Print("ret "); + PrintHex(*reinterpret_cast(data+1)); + data += 3; + break; + + case 0x69: // fall through + case 0x6B: + { int mod, regop, rm; + GetModRm(*(data+1), &mod, ®op, &rm); + int32_t imm = + *data == 0x6B ? *(data+2) : *reinterpret_cast(data+2); + Print("imul "); + PrintCPURegister(regop); + Print(","); + PrintCPURegister(rm); + Print(","); + PrintHex(imm); + data += 2 + (*data == 0x6B ? 1 : 4); + } + break; + + case 0xF6: + { int mod, regop, rm; + GetModRm(*(data+1), &mod, ®op, &rm); + if ((mod == 3) && (regop == eax)) { + Print("test_b "); + PrintCPURegister(rm); + Print(","); + PrintHex(*(data+2)); + } else { + UNIMPLEMENTED(); + } + data += 3; + } + break; + + case 0x81: // fall through + case 0x83: // 0x81 with sign extension bit set + data += PrintImmediateOp(data); + break; + + case 0x0F: + { uint8_t f0byte = *(data+1); + const char* f0mnem = F0Mnem(f0byte); + if (f0byte == 0xA2 || f0byte == 0x31) { + Print(f0mnem); + data += 2; + } else if ((f0byte & 0xF0) == 0x80) { + data += JumpConditional(data, branch_hint); + } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 || + f0byte == 0xB7 || f0byte == 0xAF) { + data += 2; + data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data); + } else if (f0byte == 0x57) { + data += 2; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + Print(f0mnem); + Print(" "); + PrintXmmRegister(regop); + Print(","); + data += PrintRightOperand(data); + } else if (f0byte == 0xB1) { + data += 2; + data += PrintOperands(f0mnem, OPER_REG_OP_ORDER, data); + } else if ((f0byte & 0xF0) == 0x90) { + data += SetCC(data); + } else if ((f0byte & 0xF0) == 0x40) { + data += CMov(data); + } else if (f0byte == 0x2F) { + data += 2; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + Print("comiss "); + PrintXmmRegister(regop); + Print(","); + PrintXmmRegister(rm); + data++; + } else { + data += 2; + if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) { + // shrd, shld, bts + Print(f0mnem); + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + data += PrintRightOperand(data); + if (f0byte == 0xAB) { + Print(","); + PrintCPURegister(regop); + } else { + Print(","); + PrintCPURegister(regop); + Print(",cl"); + } + } else { + UNIMPLEMENTED(); + } + } + } + break; + + case 0x8F: + { data++; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + if (regop == eax) { + Print("pop "); + data += PrintRightOperand(data); + } + } + break; + + case 0xFF: + { data++; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + const char* mnem = NULL; + switch (regop) { + case esi: mnem = "push"; break; + case eax: mnem = "inc"; break; + case ecx: mnem = "dec"; break; + case edx: mnem = "call"; break; + case esp: mnem = "jmp"; break; + default: mnem = "??? 0xFF"; + } + Print(mnem); + Print(" "); + data += PrintRightOperand(data); + } + break; + + case 0xC7: // imm32, fall through + case 0xC6: // imm8 + { bool is_byte = *data == 0xC6; + data++; + Print(is_byte ? "mov_b" : "mov"); + Print(" "); + data += PrintRightOperand(data); + int32_t imm = is_byte ? *data : *reinterpret_cast(data); + Print(","); + PrintHex(imm); + data += is_byte ? 1 : 4; + } + break; + + case 0x80: + { data++; + Print("cmpb "); + data += PrintRightOperand(data); + int32_t imm = *data; + Print(","); + PrintHex(imm); + data++; + } + break; + + case 0x88: // 8bit, fall through + case 0x89: // 32bit + { bool is_byte = *data == 0x88; + int mod, regop, rm; + data++; + GetModRm(*data, &mod, ®op, &rm); + Print(is_byte ? "mov_b" : "mov"); + Print(" "); + data += PrintRightOperand(data); + Print(","); + PrintCPURegister(regop); + } + break; + + case 0x66: // prefix + data++; + if (*data == 0x8B) { + data++; + data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data); + } else if (*data == 0x89) { + data++; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + Print("mov_w "); + data += PrintRightOperand(data); + Print(","); + PrintCPURegister(regop); + } else if (*data == 0x0F) { + data++; + if (*data == 0x2F) { + data++; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + Print("comisd "); + PrintXmmRegister(regop); + Print(","); + PrintXmmRegister(rm); + data++; + } else if (*data == 0X6E) { + data++; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + Print("movd "); + PrintXmmRegister(regop); + Print(","); + PrintCPURegister(rm); + data++; + } else if (*data == 0X7E) { + data++; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + Print("movd "); + PrintCPURegister(rm); + Print(","); + PrintXmmRegister(regop); + data++; + } else if (*data == 0x57 || *data == 0x54) { + const char* mnem = (*data == 0x57) ? "xorpd" : "andpd"; + data++; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + Print(mnem); + Print(" "); + PrintXmmRegister(regop); + Print(","); + data += PrintRightXmmOperand(data); + } else { + UNIMPLEMENTED(); + } + } else { + UNIMPLEMENTED(); + } + break; + + case 0xFE: + { data++; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + if (mod == 3 && regop == ecx) { + Print("dec_b "); + PrintCPURegister(rm); + } else { + UNIMPLEMENTED(); + } + data++; + } + break; + + case 0x68: + Print("push "); + PrintHex(*reinterpret_cast(data+1)); + data += 5; + break; + + case 0x6A: + Print("push "); + PrintHex(*reinterpret_cast(data + 1)); + data += 2; + break; + + case 0xA8: + Print("test al,"); + PrintHex(*reinterpret_cast(data+1)); + data += 2; + break; + + case 0xA9: + Print("test eax,"); + PrintHex(*reinterpret_cast(data+1)); + CheckPrintStop(data); + data += 5; + break; + + case 0xD1: // fall through + case 0xD3: // fall through + case 0xC1: + data += D1D3C1Instruction(data); + break; + + case 0xD9: // fall through + case 0xDA: // fall through + case 0xDB: // fall through + case 0xDC: // fall through + case 0xDD: // fall through + case 0xDE: // fall through + case 0xDF: + data += FPUInstruction(data); + break; + + case 0xEB: + data += JumpShort(data); + break; + + case 0xF3: + if (*(data+1) == 0x0F) { + uint8_t b2 = *(data+2); + switch (b2) { + case 0x2C: { + data += 3; + data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data); + break; + } + case 0x2A: { + data += 3; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + Print("cvtsi2ss "); + PrintXmmRegister(regop); + Print(","); + data += PrintRightOperand(data); + break; + } + case 0x2D: { + data += 3; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + Print("cvtss2si "); + PrintCPURegister(regop); + Print(","); + data += PrintRightXmmOperand(data); + break; + } + case 0x11: { + // movss xmm <- address + Print("movss "); + data += 3; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + data += PrintRightXmmOperand(data); + Print(","); + PrintXmmRegister(regop); + break; + } + case 0x10: { + // movss address <- xmm + data += 3; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + Print("movss "); + PrintXmmRegister(regop); + Print(","); + data += PrintRightOperand(data); + break; + } + case 0x51: // Fall through. + case 0x58: // Fall through. + case 0x59: // Fall through. + case 0x5A: // Fall through. + case 0x5C: // Fall through. + case 0x5E: // Fall through. + case 0xE6: { + // divss xmm, xmm + data += 3; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + const char* mnem = "?? 0xF3"; + switch (b2) { + case 0x51: mnem = "sqrtss"; break; + case 0x58: mnem = "addss"; break; + case 0x59: mnem = "mulss"; break; + case 0x5A: mnem = "cvtss2sd"; break; + case 0x5C: mnem = "subss"; break; + case 0x5E: mnem = "divss"; break; + case 0xE6: mnem = "cvtdq2pd"; break; + default: UNIMPLEMENTED(); + } + Print(mnem); + Print(" "); + PrintXmmRegister(regop); + Print(","); + data += PrintRightXmmOperand(data); + break; + } + default: + UNIMPLEMENTED(); + } + } + break; + case 0xF2: { + if (*(data+1) == 0x0F) { + uint8_t b2 = *(data+2); + if (b2 == 0x11) { + Print("movsd "); + data += 3; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + data += PrintRightXmmOperand(data); + Print(","); + PrintXmmRegister(regop); + } else if (b2 == 0x10) { + data += 3; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + Print("movsd "); + PrintXmmRegister(regop); + Print(","); + data += PrintRightOperand(data); + } else { + const char* mnem = "? 0xF2"; + switch (b2) { + case 0x2A: mnem = "cvtsi2sd"; break; + case 0x2D: mnem = "cvtsd2i"; break; + case 0x51: mnem = "sqrtsd"; break; + case 0x58: mnem = "addsd"; break; + case 0x59: mnem = "mulsd"; break; + case 0x5A: mnem = "cvtsd2ss"; break; + case 0x5C: mnem = "subsd"; break; + case 0x5E: mnem = "divsd"; break; + } + data += 3; + int mod, regop, rm; + GetModRm(*data, &mod, ®op, &rm); + if (b2 == 0x2A) { + Print(mnem); + Print(" "); + PrintXmmRegister(regop); + Print(","); + data += PrintRightOperand(data); + } else if (b2 == 0x2D) { + Print(mnem); + Print(" "); + PrintCPURegister(regop); + Print(","); + PrintXmmRegister(rm); + data++; + } else { + Print(mnem); + Print(" "); + PrintXmmRegister(regop); + Print(","); + data += PrintRightXmmOperand(data); + } + } + } else { + UNIMPLEMENTED(); + } + break; + } + case 0xF7: + data += F7Instruction(data); + break; + + case 0xC8: + data += DecodeEnter(data); + break; + + default: + OS::Print("Unknown case 0x%x\n", *data); + UNIMPLEMENTED(); + } + } + + int instr_len = data - reinterpret_cast(pc); + ASSERT(instr_len > 0); // Ensure progress. + + return instr_len; +} + + +int Disassembler::DecodeInstruction(char* hex_buffer, intptr_t hex_size, + char* human_buffer, intptr_t human_size, + uword pc) { + ASSERT(hex_size > 0); + ASSERT(human_size > 0); + X86Decoder decoder(human_buffer, human_size); + int instruction_length = decoder.InstructionDecode(pc); + uint8_t* pc_ptr = reinterpret_cast(pc); + int hex_index = 0; + int remaining_size = hex_size - hex_index; + for (int i = 0; (i < instruction_length) && (remaining_size > 2); ++i) { + OS::SNPrint(&hex_buffer[hex_index], remaining_size, "%02x", pc_ptr[i]); + hex_index += 2; + remaining_size -= 2; + } + hex_buffer[hex_index] = '\0'; + return instruction_length; +} + + +const char* Disassembler::RegisterName(Register reg) { + ASSERT(0 <= reg); + ASSERT(reg < kMaxCPURegisters); + return (cpu_regs[reg]); +} + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/disassembler_test.cc b/runtime/vm/disassembler_test.cc new file mode 100644 index 00000000000..caccb2e250e --- /dev/null +++ b/runtime/vm/disassembler_test.cc @@ -0,0 +1,27 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assembler.h" +#include "vm/disassembler.h" +#include "vm/unit_test.h" +#include "vm/virtual_memory.h" + +namespace dart { + +#if defined(TARGET_ARCH_IA32) // Disassembler only supported on IA32 now. +TEST_CASE(Disassembler) { + Assembler assembler; + // The used instructions work on all platforms. + Register reg = static_cast(0); + assembler.AddImmediate(reg, Immediate(1)); + assembler.AddImmediate(reg, Immediate(3)); + + // Only verify that the disassembler does not crash. + AssemblerTest test("Disassembler", &assembler); + uword entry = test.Assemble(); + Disassembler::Disassemble(entry, entry + assembler.CodeSize()); +} +#endif + +} // namespace dart diff --git a/runtime/vm/disassembler_x64.cc b/runtime/vm/disassembler_x64.cc new file mode 100644 index 00000000000..e84e5a018a7 --- /dev/null +++ b/runtime/vm/disassembler_x64.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_X64. +#if defined(TARGET_ARCH_X64) + +#include "vm/disassembler.h" + +#include "vm/assert.h" + +namespace dart { + +int Disassembler::DecodeInstruction(char* hexa_buffer, intptr_t hexa_size, + char* human_buffer, intptr_t human_size, + uword pc) { + UNIMPLEMENTED(); + return 0; +} + + +const char* Disassembler::RegisterName(Register reg) { + UNIMPLEMENTED(); + return NULL; +} + +} // namespace dart + +#endif // defined TARGET_ARCH_X64 diff --git a/runtime/vm/double_internals.h b/runtime/vm/double_internals.h new file mode 100644 index 00000000000..2465ffb6107 --- /dev/null +++ b/runtime/vm/double_internals.h @@ -0,0 +1,80 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_DOUBLE_INTERNALS_H_ +#define VM_DOUBLE_INTERNALS_H_ + +#include "vm/utils.h" + +namespace dart { + +// We assume that doubles and uint64_t have the same endianness. +static uint64_t double_to_uint64(double d) { return bit_cast(d); } + +// Helper functions for doubles. +class DoubleInternals { + public: + static const int kSignificandSize = 53; + + explicit DoubleInternals(double d) : d64_(double_to_uint64(d)) {} + + // Returns the double's bit as uint64. + uint64_t AsUint64() const { + return d64_; + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint64_t d64 = AsUint64(); + int biased_e = + static_cast((d64 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint64_t Significand() const { + uint64_t d64 = AsUint64(); + uint64_t significand = d64 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the double is a denormal. + bool IsDenormal() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == kExponentMask; + } + + int Sign() const { + uint64_t d64 = AsUint64(); + return (d64 & kSignMask) == 0? 1: -1; + } + + private: + static const uint64_t kSignMask = DART_2PART_UINT64_C(0x80000000, 00000000); + static const uint64_t kExponentMask = + DART_2PART_UINT64_C(0x7FF00000, 00000000); + static const uint64_t kSignificandMask = + DART_2PART_UINT64_C(0x000FFFFF, FFFFFFFF); + static const uint64_t kHiddenBit = DART_2PART_UINT64_C(0x00100000, 00000000); + static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. + static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + + const uint64_t d64_; +}; + +} // namespace dart + +#endif // VM_DOUBLE_INTERNALS_H_ diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc new file mode 100644 index 00000000000..2904ec871b6 --- /dev/null +++ b/runtime/vm/exceptions.cc @@ -0,0 +1,162 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/exceptions.h" + +#include "vm/cpu.h" +#include "vm/dart_entry.h" +#include "vm/flags.h" +#include "vm/object.h" +#include "vm/stack_frame.h" + +namespace dart { + +DEFINE_FLAG(bool, print_stack_trace_at_throw, false, + "Prints a stack trace everytime a throw occurs."); + +// Iterate through the stack frames and try to find a frame with an +// exception handler. Once found, set the pc, sp and fp so that execution +// can continue in that frame. +static bool FindExceptionHandler(uword* handler_pc, + uword* handler_sp, + uword* handler_fp, + GrowableArray* stack_frame_pcs) { + StackFrameIterator frames(StackFrameIterator::kDontValidateFrames); + StackFrame* frame = frames.NextFrame(); + ASSERT(frame != NULL); + while (!frame->IsEntryFrame()) { + if (frame->IsDartFrame()) { + stack_frame_pcs->Add(frame->pc()); + DartFrame* dart_frame = reinterpret_cast(frame); + if (dart_frame->FindExceptionHandler(handler_pc)) { + *handler_sp = frame->sp(); + *handler_fp = frame->fp(); + return true; + } + } + frame = frames.NextFrame(); + ASSERT(frame != NULL); + } + ASSERT(frame->IsEntryFrame()); + *handler_pc = frame->pc(); + *handler_sp = frame->sp(); + *handler_fp = frame->fp(); + return false; +} + + +static void ThrowExceptionHelper(const Instance& exception, + const Instance& existing_stacktrace) { + uword handler_pc = 0; + uword handler_sp = 0; + uword handler_fp = 0; + GrowableArray stack_frame_pcs; + bool handler_exists = FindExceptionHandler(&handler_pc, + &handler_sp, + &handler_fp, + &stack_frame_pcs); + // TODO(5411263): At some point we can optimize by figuring out if a + // stack trace is needed based on whether the catch code specifies a + // stack trace object or there is a rethrow in the catch clause. + ASSERT(stack_frame_pcs.length() > 0); // At least one dart frame must exist. + Stacktrace& stacktrace = Stacktrace::Handle(); + if (existing_stacktrace.IsNull()) { + stacktrace = Stacktrace::New(stack_frame_pcs); + } else { + stacktrace ^= existing_stacktrace.raw(); + stacktrace.Append(stack_frame_pcs); + } + if (FLAG_print_stack_trace_at_throw) { + OS::Print("Exception '%s' thrown:\n", exception.ToCString()); + OS::Print("%s\n", stacktrace.ToCString()); + } + if (handler_exists) { + // Found a dart handler for the exception, jump to it. + CPU::JumpToExceptionHandler(handler_pc, + handler_sp, + handler_fp, + exception, + stacktrace); + } else { + // No dart exception handler found in this invocation sequence, + // so we create an unhandled exception object and return to the + // invocation stub so that it returns this unhandled exception + // object. The C++ code which invoked this dart sequence can check + // and do the appropriate thing (rethrow the exception to the + // dart invocation sequence above it, print diagnostics and terminate + // the isolate etc.). + const UnhandledException& unhandled_exception = UnhandledException::Handle( + UnhandledException::New(exception, stacktrace)); + CPU::JumpToUnhandledExceptionHandler(handler_pc, + handler_sp, + handler_fp, + unhandled_exception); + } + UNREACHABLE(); +} + + +void Exceptions::Throw(const Instance& exception) { + ASSERT(!exception.IsNull()); + ThrowExceptionHelper(exception, Instance::Handle()); +} + + +void Exceptions::ReThrow(const Instance& exception, + const Instance& stacktrace) { + ASSERT(!exception.IsNull()); + ThrowExceptionHelper(exception, stacktrace); +} + + +void Exceptions::ThrowByType( + ExceptionType type, const GrowableArray& arguments) { + const Instance& exception = Instance::Handle(Create(type, arguments)); + Throw(exception); +} + + +RawInstance* Exceptions::Create( + ExceptionType type, const GrowableArray& arguments) { + String& class_name = String::Handle(); + switch (type) { + case kIndexOutOfRange: + class_name = String::NewSymbol("IndexOutOfRangeException"); + break; + case kIllegalArgument: + class_name = String::NewSymbol("IllegalArgumentException"); + break; + case kNoSuchMethod: + class_name = String::NewSymbol("NoSuchMethodException"); + break; + case kClosureArgumentMismatch: + class_name = String::NewSymbol("ClosureArgumentMismatchException"); + break; + case kObjectNotClosure: + class_name = String::NewSymbol("ObjectNotClosureException"); + break; + case kBadNumberFormat: + class_name = String::NewSymbol("BadNumberFormatException"); + break; + case kStackOverflow: + class_name = String::NewSymbol("StackOverflowException"); + break; + case kWrongArgumentCount: + class_name = String::NewSymbol("WrongArgumentCountException"); + break; + case kInternalError: + class_name = String::NewSymbol("InternalError"); + break; + case kNullPointer: + class_name = String::NewSymbol("NullPointerException"); + break; + case kIllegalJSRegExp: + class_name = String::NewSymbol("IllegalJSRegExpException"); + break; + } + + return DartLibraryCalls::ExceptionCreate(class_name, arguments); +} + +} // namespace dart diff --git a/runtime/vm/exceptions.h b/runtime/vm/exceptions.h new file mode 100644 index 00000000000..5027519b409 --- /dev/null +++ b/runtime/vm/exceptions.h @@ -0,0 +1,48 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_EXCEPTIONS_H_ +#define VM_EXCEPTIONS_H_ + +#include "vm/allocation.h" +#include "vm/growable_array.h" + +namespace dart { + +// Forward declarations. +class Instance; +class Object; +class RawInstance; + +class Exceptions : AllStatic { + public: + static void Throw(const Instance& exception); + static void ReThrow(const Instance& exception, const Instance& stacktrace); + + enum ExceptionType { + kIndexOutOfRange, + kIllegalArgument, + kNoSuchMethod, + kClosureArgumentMismatch, + kObjectNotClosure, + kBadNumberFormat, + kStackOverflow, + kWrongArgumentCount, + kInternalError, + kNullPointer, + kIllegalJSRegExp, + }; + + static void ThrowByType(ExceptionType type, + const GrowableArray& arguments); + static RawInstance* Create(ExceptionType type, + const GrowableArray& arguments); + + private: + DISALLOW_COPY_AND_ASSIGN(Exceptions); +}; + +} // namespace dart + +#endif // VM_EXCEPTIONS_H_ diff --git a/runtime/vm/exceptions_test.cc b/runtime/vm/exceptions_test.cc new file mode 100644 index 00000000000..d3e4e1d2310 --- /dev/null +++ b/runtime/vm/exceptions_test.cc @@ -0,0 +1,141 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "include/dart_api.h" + +#include "vm/assert.h" +#include "vm/dart_api_impl.h" +#include "vm/unit_test.h" + +namespace dart { + +#if defined(TARGET_ARCH_IA32) // only ia32 can run exception tests. +#define FUNCTION_NAME(name) UnhandledExcp_##name +#define REGISTER_FUNCTION(name, count) \ + { ""#name, FUNCTION_NAME(name), count }, + + +void FUNCTION_NAME(Unhandled_equals)(Dart_NativeArguments args) { + NativeArguments* arguments = reinterpret_cast(args); + const Instance& expected = Instance::CheckedHandle(arguments->At(0)); + const Instance& actual = Instance::CheckedHandle(arguments->At(1)); + if (!expected.Equals(actual)) { + OS::Print("expected: '%s' actual: '%s'\n", + expected.ToCString(), actual.ToCString()); + FATAL("Unhandled_equals fails.\n"); + } +} + + +void FUNCTION_NAME(Unhandled_invoke)(Dart_NativeArguments args) { + // Invoke the specified entry point. + Dart_Result result = Dart_InvokeStatic(TestCase::lib(), + Dart_NewString("Second"), + Dart_NewString("method2"), + 0, + NULL); + ASSERT(Dart_IsValidResult(result)); + ASSERT(Dart_ExceptionOccurred(Dart_GetResult(result))); + return; +} + + +void FUNCTION_NAME(Unhandled_invoke2)(Dart_NativeArguments args) { + // Invoke the specified entry point. + Dart_Result result = Dart_InvokeStatic(TestCase::lib(), + Dart_NewString("Second"), + Dart_NewString("method2"), + 0, + NULL); + ASSERT(Dart_IsValidResult(result)); + Dart_Handle retobj = Dart_GetResult(result); + ASSERT(Dart_ExceptionOccurred(retobj)); + result = Dart_GetException(retobj); + ASSERT(Dart_IsValidResult(result)); + Dart_Handle exception = Dart_GetResult(result); + Dart_ThrowException(exception); + UNREACHABLE(); + return; +} + + +// List all native functions implemented in the vm or core boot strap dart +// libraries so that we can resolve the native function to it's entry +// point. +#define UNHANDLED_NATIVE_LIST(V) \ + V(Unhandled_equals, 2) \ + V(Unhandled_invoke, 0) \ + V(Unhandled_invoke2, 0) \ + + +static struct NativeEntries { + const char* name_; + Dart_NativeFunction function_; + int argument_count_; +} BuiltinEntries[] = { + UNHANDLED_NATIVE_LIST(REGISTER_FUNCTION) +}; + + +static Dart_NativeFunction native_lookup(Dart_Handle name, + int argument_count) { + const Object& obj = Object::Handle(Api::UnwrapHandle(name)); + ASSERT(obj.IsString()); + const char* function_name = obj.ToCString(); + ASSERT(function_name != NULL); + int num_entries = sizeof(BuiltinEntries) / sizeof(struct NativeEntries); + for (int i = 0; i < num_entries; i++) { + struct NativeEntries* entry = &(BuiltinEntries[i]); + if (!strcmp(function_name, entry->name_)) { + return reinterpret_cast(entry->function_); + } + } + return NULL; +} + + +// Unit test case to verify unhandled exceptions. +TEST_CASE(UnhandledExceptions) { + const char* kScriptChars = + "class UnhandledExceptions {\n" + " static equals(var obj1, var obj2) native \"Unhandled_equals\";" + " static invoke() native \"Unhandled_invoke\";\n" + " static invoke2() native \"Unhandled_invoke2\";\n" + "}\n" + "class Second {\n" + " Second() { }\n" + " static int method1(int param) {\n" + " UnhandledExceptions.invoke();\n" + " return 2;\n" + " }\n" + " static int method2() {\n" + " throw new Second();\n" + " }\n" + " static int method3(int param) {\n" + " try {\n" + " UnhandledExceptions.invoke2();\n" + " } catch (Second e) {\n" + " return 3;\n" + " }\n" + " return 2;\n" + " }\n" + "}\n" + "class UnhandledExceptionTest {\n" + " static testMain() {\n" + " UnhandledExceptions.equals(2, Second.method1(1));\n" + " UnhandledExceptions.equals(3, Second.method3(1));\n" + " }\n" + "}"; + Dart_Handle lib = TestCase::LoadTestScript( + kScriptChars, + reinterpret_cast(native_lookup)); + Dart_InvokeStatic(lib, + Dart_NewString("UnhandledExceptionTest"), + Dart_NewString("testMain"), + 0, + NULL); +} +#endif // TARGET_ARCH_IA32. + +} // namespace dart diff --git a/runtime/vm/flags.cc b/runtime/vm/flags.cc new file mode 100644 index 00000000000..50cdf620c65 --- /dev/null +++ b/runtime/vm/flags.cc @@ -0,0 +1,268 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/flags.h" + +#include "vm/assert.h" +#include "vm/os.h" + +namespace dart { + +DEFINE_FLAG(bool, print_flags, false, "Print flags as they are being parsed."); +DEFINE_FLAG(bool, ignore_unrecognized_flags, false, + "Ignore unrecognized flags."); + +// List of registered flags. +Flag* Flags::flags_ = NULL; + +class Flag { + public: + enum Type { + kBoolean, + kInteger, + kString, + kNumFlagTypes + }; + + Flag(const char* name, const char* comment, void* addr, Type type) + : name_(name), comment_(comment), addr_(addr), type_(type) { + } + + void Print() { + if (IsUnrecognized()) { + OS::Print("%s: unrecognized\n", name_); + return; + } + switch (type_) { + case kBoolean: { + OS::Print("%s: %s\n", name_, *this->bool_ptr_ ? "true" : "false"); + break; + } + case kInteger: { + OS::Print("%s: %d\n", name_, *this->int_ptr_); + break; + } + case kString: { + if (*this->charp_ptr_ != NULL) { + OS::Print("%s: '%s'\n", name_, *this->charp_ptr_); + } else { + OS::Print("%s: (null)\n", name_); + } + break; + } + default: + UNREACHABLE(); + break; + } + } + + bool IsUnrecognized() const { + return (type_ == kBoolean) && (bool_ptr_ == NULL); + } + + Flag* next_; + const char* name_; + const char* comment_; + union { + void* addr_; + bool* bool_ptr_; + int* int_ptr_; + charp* charp_ptr_; + }; + Type type_; +}; + + +Flag* Flags::Lookup(const char* name) { + Flag* cur = Flags::flags_; + while (cur != NULL) { + if (strcmp(cur->name_, name) == 0) { + return cur; + } + cur = cur->next_; + } + return NULL; +} + + +bool Flags::Register_bool(bool* addr, + const char* name, + bool default_value, + const char* comment) { + ASSERT(Lookup(name) == NULL); + + Flag* flag = new Flag(name, comment, addr, Flag::kBoolean); + flag->next_ = Flags::flags_; + Flags::flags_ = flag; + + return default_value; +} + + +int Flags::Register_int(int* addr, + const char* name, + int default_value, + const char* comment) { + ASSERT(Lookup(name) == NULL); + + Flag* flag = new Flag(name, comment, addr, Flag::kInteger); + flag->next_ = Flags::flags_; + Flags::flags_ = flag; + + return default_value; +} + + +const char* Flags::Register_charp(charp* addr, + const char* name, + const char* default_value, + const char* comment) { + ASSERT(Lookup(name) == NULL); + Flag* flag = new Flag(name, comment, addr, Flag::kString); + flag->next_ = Flags::flags_; + Flags::flags_ = flag; + return default_value; +} + + +static void Normalize(char* s) { + intptr_t len = strlen(s); + for (intptr_t i = 0; i < len; i++) { + if (s[i] == '-') { + s[i] = '_'; + } + } +} + + +void Flags::Parse(const char* option) { + // Find the beginning of the option argument, if it exists. + const char* equals = option; + while ((*equals != '\0') && (*equals != '=')) { + equals++; + } + + const char* argument = NULL; + + // Determine if this is an option argument. + if (*equals != '=') { + // No explicit option argument. Determine if there is a "no_" prefix + // preceding the name. + const char* kNoPrefix = "no_"; + const intptr_t kNoPrefixLen = strlen(kNoPrefix); + if (strncmp(option, kNoPrefix, kNoPrefixLen) == 0) { + option += kNoPrefixLen; // Skip the "no_" when looking up the name. + argument = "false"; + } else { + argument = "true"; + } + } else { + // The argument for the option starts right after the equals sign. + argument = equals + 1; + } + + // Initialize the flag name. + intptr_t name_len = equals - option; + char* name = new char[name_len + 1]; + strncpy(name, option, name_len); + name[name_len] = '\0'; + Normalize(name); + + Flag* flag = Flags::Lookup(name); + if (flag == NULL) { + // Collect unrecognized flags. + char* new_flag = new char[name_len + 1]; + strncpy(new_flag, name, name_len); + new_flag[name_len] = '\0'; + Flags::Register_bool(NULL, new_flag, true, NULL); + } else { + // Only set values for recognized flags, skip collected + // unrecognized flags. + if (!flag->IsUnrecognized()) { + switch (flag->type_) { + case Flag::kBoolean: { + if (strcmp(argument, "true") == 0) { + *flag->bool_ptr_ = true; + } else if (strcmp(argument, "false") == 0) { + *flag->bool_ptr_ = false; + } else { + OS::Print("Ignoring flag: %s is a bool flag.\n", name); + } + break; + } + case Flag::kString: { + *flag->charp_ptr_ = argument == NULL ? NULL : strdup(argument); + break; + } + case Flag::kInteger: { + char* endptr = NULL; + int val = strtol(argument, &endptr, 10); + if (endptr != argument) { + *flag->int_ptr_ = val; + } + break; + } + default: { + UNREACHABLE(); + break; + } + } + } + } + + delete[] name; +} + + +static bool IsValidFlag(const char* name, + const char* prefix, + intptr_t prefix_length) { + intptr_t name_length = strlen(name); + return ((name_length > prefix_length) && + (strncmp(name, prefix, prefix_length) == 0)); +} + + +void Flags::ProcessCommandLineFlags(int number_of_vm_flags, char** vm_flags) { + const char* kPrefix = "--"; + const intptr_t kPrefixLen = strlen(kPrefix); + + int i = 0; + while ((i < number_of_vm_flags) && + IsValidFlag(vm_flags[i], kPrefix, kPrefixLen)) { + const char* option = vm_flags[i] + kPrefixLen; + Parse(option); + i++; + } + + if (!FLAG_ignore_unrecognized_flags) { + int unrecognized_count = 0; + Flag* flag = Flags::flags_; + while (flag != NULL) { + if (flag->IsUnrecognized()) { + if (unrecognized_count == 0) { + OS::PrintErr("Unrecognized flags: %s", flag->name_); + } else { + OS::PrintErr(", %s", flag->name_); + } + unrecognized_count++; + } + flag = flag->next_; + } + if (unrecognized_count > 0) { + OS::PrintErr("\n"); + exit(255); + } + } + if (FLAG_print_flags) { + OS::Print("Flag settings:\n"); + Flag* flag = Flags::flags_; + while (flag != NULL) { + flag->Print(); + flag = flag->next_; + } + } +} + +} // namespace dart diff --git a/runtime/vm/flags.h b/runtime/vm/flags.h new file mode 100644 index 00000000000..6e3ee30a895 --- /dev/null +++ b/runtime/vm/flags.h @@ -0,0 +1,72 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_FLAGS_H_ +#define VM_FLAGS_H_ + +#include "vm/assert.h" +#include "vm/globals.h" + +typedef const char* charp; + +#define DECLARE_FLAG(type, name) \ + extern type FLAG_##name + +#define DEFINE_FLAG(type, name, default_value, comment) \ + type FLAG_##name = Flags::Register_##type(&FLAG_##name, \ + #name, \ + default_value, \ + comment) + + +#if defined(DEBUG) +#define DECLARE_DEBUG_FLAG(type, name) DECLARE_FLAG(type, name) +#define DEFINE_DEBUG_FLAG(type, name, default_value, comment) \ + DEFINE_FLAG(type, name, default_value, comment) +#else +#define DECLARE_DEBUG_FLAG(type, name) +#define DEFINE_DEBUG_FLAG(type, name, default_value, comment) +#endif + +namespace dart { + +// Forward declaration. +class Flag; + +class Flags { + public: + static bool Register_bool(bool* addr, + const char* name, + bool default_value, + const char* comment); + + static int Register_int(int* addr, + const char* name, + int default_value, + const char* comment); + + static const char* Register_charp(charp* addr, + const char* name, + const char* default_value, + const char* comment); + + static void ProcessCommandLineFlags(int argc, char** argv); + + static Flag* Lookup(const char* name); + + private: + static Flag* flags_; + + static void Parse(const char* option); + + // Testing needs direct access to private methods. + friend void Dart_TestParseFlags(); + + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(Flags); +}; + +} // namespace dart + +#endif // VM_FLAGS_H_ diff --git a/runtime/vm/flags_test.cc b/runtime/vm/flags_test.cc new file mode 100644 index 00000000000..3a3813d701b --- /dev/null +++ b/runtime/vm/flags_test.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/flags.h" +#include "vm/heap.h" +#include "vm/unit_test.h" + +namespace dart { + +DEFINE_FLAG(bool, basic_flag, true, "Testing of a basic boolean flag."); + +DECLARE_FLAG(bool, print_flags); + +UNIT_TEST_CASE(BasicFlags) { + EXPECT_EQ(true, FLAG_basic_flag); + EXPECT_EQ(false, FLAG_verbose_gc); + EXPECT_EQ(false, FLAG_print_flags); +} + + +DEFINE_FLAG(bool, parse_flag_bool_test, true, "Flags::Parse (bool) testing"); +DEFINE_FLAG(charp, string_opt_test, NULL, "Testing: string option."); +DEFINE_FLAG(charp, entrypoint_test, "main", "Testing: entrypoint"); +DEFINE_FLAG(int, counter, 100, "Testing: int flag"); + +UNIT_TEST_CASE(ParseFlags) { + EXPECT_EQ(true, FLAG_parse_flag_bool_test); + Flags::Parse("no_parse_flag_bool_test"); + EXPECT_EQ(false, FLAG_parse_flag_bool_test); + Flags::Parse("parse_flag_bool_test"); + EXPECT_EQ(true, FLAG_parse_flag_bool_test); + Flags::Parse("parse_flag_bool_test=false"); + EXPECT_EQ(false, FLAG_parse_flag_bool_test); + Flags::Parse("parse_flag_bool_test=true"); + EXPECT_EQ(true, FLAG_parse_flag_bool_test); + + EXPECT_EQ(true, FLAG_string_opt_test == NULL); + Flags::Parse("string_opt_test=doobidoo"); + EXPECT_EQ(true, FLAG_string_opt_test != NULL); + EXPECT_EQ(0, strcmp(FLAG_string_opt_test, "doobidoo")); + + EXPECT_EQ(true, FLAG_entrypoint_test != NULL); + EXPECT_EQ(0, strcmp(FLAG_entrypoint_test, "main")); + + EXPECT_EQ(100, FLAG_counter); + Flags::Parse("counter=-300"); + EXPECT_EQ(-300, FLAG_counter); + Flags::Parse("counter=$300"); + EXPECT_EQ(-300, FLAG_counter); +} + +} // namespace dart diff --git a/runtime/vm/gdbjit_linux.cc b/runtime/vm/gdbjit_linux.cc new file mode 100644 index 00000000000..9fac94ef5e2 --- /dev/null +++ b/runtime/vm/gdbjit_linux.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include + +#include "vm/gdbjit_linux.h" + +extern "C" { + typedef enum { + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN + } jit_actions_t; + + struct jit_code_entry { + struct jit_code_entry *next_entry; + struct jit_code_entry *prev_entry; + const char *symfile_addr; + uint64_t symfile_size; + }; + + struct jit_descriptor { + uint32_t version; + /* This type should be jit_actions_t, but we use uint32_t + to be explicit about the bitwidth. */ + uint32_t action_flag; + struct jit_code_entry *relevant_entry; + struct jit_code_entry *first_entry; + }; + + /* GDB puts a breakpoint in this function. */ + void __attribute__((noinline)) __jit_debug_register_code() { } + + /* Make sure to specify the version statically, because the + debugger may check the version before we can set it. */ + struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 }; + + static struct jit_code_entry* first_dynamic_region = NULL; + static struct jit_code_entry* last_dynamic_region = NULL; + + void addDynamicSection(const char* symfile_addr, uint64_t symfile_size) { + jit_code_entry* new_entry = reinterpret_cast( + malloc(sizeof(jit_code_entry))); + if (new_entry != NULL) { + new_entry->symfile_addr = symfile_addr; + new_entry->symfile_size = symfile_size; + new_entry->next_entry = NULL; + new_entry->prev_entry = last_dynamic_region; + if (first_dynamic_region == NULL) { + first_dynamic_region = new_entry; + } else { + last_dynamic_region->next_entry = new_entry; + } + last_dynamic_region = new_entry; + } + __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; + __jit_debug_descriptor.relevant_entry = new_entry; + __jit_debug_descriptor.first_entry = first_dynamic_region; + __jit_debug_register_code(); + } + + void deleteDynamicSections() { + struct jit_code_entry* iterator = last_dynamic_region; + while (iterator != NULL) { + __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN; + __jit_debug_descriptor.relevant_entry = iterator; + __jit_debug_descriptor.first_entry = first_dynamic_region; + __jit_debug_register_code(); + iterator = iterator->prev_entry; + } + first_dynamic_region = NULL; + last_dynamic_region = NULL; + } +}; diff --git a/runtime/vm/gdbjit_linux.h b/runtime/vm/gdbjit_linux.h new file mode 100644 index 00000000000..4d8d369f968 --- /dev/null +++ b/runtime/vm/gdbjit_linux.h @@ -0,0 +1,13 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_GDBJIT_LINUX_H_ +#define VM_GDBJIT_LINUX_H_ + +extern "C" { + void addDynamicSection(const char* symfile_addr, uint64_t symfile_size); + void deleteDynamicSections(); +}; + +#endif // VM_GDBJIT_LINUX_H_ diff --git a/runtime/vm/globals.h b/runtime/vm/globals.h new file mode 100644 index 00000000000..af70f0dc6a8 --- /dev/null +++ b/runtime/vm/globals.h @@ -0,0 +1,327 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_GLOBALS_H_ +#define VM_GLOBALS_H_ + +// __STDC_FORMAT_MACROS has to be defined to enable platform independent printf. +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#if defined(_WIN32) +// Cut down on the amount of stuff that gets included via windows.h. +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#define NOKERNEL +#define NOUSER +#define NOSERVICE +#define NOSOUND +#define NOMCX + +#include + +// Undef conflicting defines. +#undef PARITY_EVEN +#undef PARITY_ODD +#undef near +#endif + +#if !defined(_WIN32) +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include "vm/c99_support_win.h" +#include "vm/inttypes_support_win.h" +#endif + +// The following #defines are invalidated. +#undef OVERFLOW // From math.h conflicts in constants_ia32.h + +namespace dart { + +// Processor architecture detection. For more info on what's defined, see: +// http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// http://www.agner.org/optimize/calling_conventions.pdf +// or with gcc, run: "echo | gcc -E -dM -" +#if defined(_M_X64) || defined(__x86_64__) +#define HOST_ARCH_X64 1 +#define ARCH_IS_64_BIT 1 +#elif defined(_M_IX86) || defined(__i386__) +#define HOST_ARCH_IA32 1 +#define ARCH_IS_32_BIT 1 +#elif defined(__ARMEL__) +#define HOST_ARCH_ARM 1 +#define ARCH_IS_32_BIT 1 +#else +#error Architecture was not detected as supported by Dart. +#endif + +#if !defined(TARGET_ARCH_ARM) +#if !defined(TARGET_ARCH_X64) +#if !defined(TARGET_ARCH_IA32) +// No target architecture specified pick the one matching the host architecture. +#if defined(HOST_ARCH_ARM) +#define TARGET_ARCH_ARM 1 +#elif defined(HOST_ARCH_X64) +#define TARGET_ARCH_X64 1 +#elif defined(HOST_ARCH_IA32) +#define TARGET_ARCH_IA32 1 +#else +#error Automatic target architecture detection failed. +#endif +#endif +#endif +#endif + +// Verify that host and target architectures match, we cannot +// have a 64 bit Dart VM generating 32 bit code or vice-versa. +#if defined(TARGET_ARCH_X64) +#if !defined(ARCH_IS_64_BIT) +#error Mismatched Host/Target architectures. +#endif +#elif defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) +#if !defined(ARCH_IS_32_BIT) +#error Mismatched Host/Target architectures. +#endif +#endif + + +// Target OS detection. +// for more information on predefined macros: +// - http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// - with gcc, run: "echo | gcc -E -dM -" +#if defined(__linux__) || defined(__FreeBSD__) +#define TARGET_OS_LINUX 1 +#elif defined(__APPLE__) +#define TARGET_OS_MACOS 1 +#elif defined(_WIN32) +#define TARGET_OS_WINDOWS 1 +#else +#error Automatic target os detection failed. +#endif + + +// Printf format for intptr_t on Windows. +#if !defined(PRIxPTR) && defined(TARGET_OS_WINDOWS) +#if defined(ARCH_IS_32_BIT) +#define PRIxPTR "x" +#else +#define PRIxPTR "llx" +#endif // defined(ARCH_IS_32_BIT) +#endif // !defined(PRIxPTR) && defined(TARGET_OS_WINDOWS) + + +// The following macro works on both 32 and 64-bit platforms. +// Usage: instead of writing 0x1234567890123456 +// write DART_2PART_UINT64_C(0x12345678,90123456); +#define DART_2PART_UINT64_C(a, b) \ + (((static_cast(a) << 32) + 0x##b##u)) + + +// Types for native machine words. Guaranteed to be able to hold pointers and +// integers. +typedef intptr_t word; +typedef uintptr_t uword; + +// A type large enough to contain the value of the C++ vtable. This is needed +// to support the handle operations. +typedef uword cpp_vtable; + +// Byte sizes. +const int kWordSize = sizeof(word); +#ifdef ARCH_IS_32_BIT +const int kWordSizeLog2 = 2; +#else +const int kWordSizeLog2 = 3; +#endif + +// Bit sizes. +const int kBitsPerByte = 8; +const int kBitsPerByteLog2 = 3; +const int kBitsPerWord = kWordSize * kBitsPerByte; + +// System-wide named constants. +const int KB = 1024; +const int MB = KB * KB; +const int GB = KB * KB * KB; + +// A macro to disallow the copy constructor and operator= functions. +// This should be used in the private: declarations for a class. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ +private: \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + + +// A macro to disallow all the implicit constructors, namely the default +// constructor, copy constructor and operator= functions. This should be +// used in the private: declarations for a class that wants to prevent +// anyone from instantiating it. This is especially useful for classes +// containing only static methods. +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ +private: \ + TypeName(); \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + + +// Macro to disallow allocation in the C++ heap. This should be used +// in the private section for a class. +#define DISALLOW_ALLOCATION() \ +public: \ + void operator delete(void* pointer) { UNREACHABLE(); } \ +private: \ + void* operator new(size_t size); + + +// The expression OFFSET_OF(type, field) computes the byte-offset of +// the specified field relative to the containing type. +// +// The expression OFFSET_OF_RETURNED_VALUE(type, accessor) computes the +// byte-offset of the return value of the accessor to the containing type. +// +// None of these use 0 or NULL, which causes a problem with the compiler +// warnings we have enabled (which is also why 'offsetof' doesn't seem to work). +// The workaround is to use the non-zero value kOffsetOfPtr. +const intptr_t kOffsetOfPtr = 32; + +#define OFFSET_OF(type, field) \ + (reinterpret_cast(&(reinterpret_cast(kOffsetOfPtr)->field)) \ + - kOffsetOfPtr) + +#define OFFSET_OF_RETURNED_VALUE(type, accessor) \ + (reinterpret_cast( \ + (reinterpret_cast(kOffsetOfPtr)->accessor())) - kOffsetOfPtr) + + +// The USE(x) template is used to silence C++ compiler warnings issued +// for unused variables. +template +static inline void USE(T) { } + + +// Use implicit_cast as a safe version of static_cast or const_cast +// for upcasting in the type hierarchy (i.e. casting a pointer to Foo +// to a pointer to SuperclassOfFoo or casting a pointer to Foo to +// a const pointer to Foo). +// When you use implicit_cast, the compiler checks that the cast is safe. +// Such explicit implicit_casts are necessary in surprisingly many +// situations where C++ demands an exact type match instead of an +// argument type convertable to a target type. +// +// The From type can be inferred, so the preferred syntax for using +// implicit_cast is the same as for static_cast etc.: +// +// implicit_cast(expr) +// +// implicit_cast would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +template +inline To implicit_cast(From const &f) { + return f; +} + + +// Use like this: down_cast(foo); +template // use like this: down_cast(foo); +inline To down_cast(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away completely. + if (false) { + implicit_cast(0); + } + return static_cast(f); +} + + +// The type-based aliasing rule allows the compiler to assume that +// pointers of different types (for some definition of different) +// never alias each other. Thus the following code does not work: +// +// float f = foo(); +// int fbits = *(int*)(&f); +// +// The compiler 'knows' that the int pointer can't refer to f since +// the types don't match, so the compiler may cache f in a register, +// leaving random data in fbits. Using C++ style casts makes no +// difference, however a pointer to char data is assumed to alias any +// other pointer. This is the 'memcpy exception'. +// +// The bit_cast function uses the memcpy exception to move the bits +// from a variable of one type to a variable of another type. Of +// course the end result is likely to be implementation dependent. +// Most compilers (gcc-4.2 and MSVC 2005) will completely optimize +// bit_cast away. +// +// There is an additional use for bit_cast. Recent gccs will warn when +// they see casts that may result in breakage due to the type-based +// aliasing rule. If you have checked that there is no breakage you +// can use bit_cast to cast one pointer type to another. This confuses +// gcc enough that it can no longer see that you have cast one pointer +// type to another thus avoiding the warning. +template +inline D bit_cast(const S& source) { + // Compile time assertion: sizeof(D) == sizeof(S). A compile error + // here means your D and S have different sizes. + typedef char VerifySizesAreEqual[sizeof(D) == sizeof(S) ? 1 : -1]; + + D destination; + // This use of memcpy is safe: source and destination cannot overlap. + memcpy(&destination, &source, sizeof(destination)); + return destination; +} + + +// Similar to bit_cast, but allows copying from types of unrelated +// sizes. This method was introduced to enable the strict aliasing +// optimizations of GCC 4.4. Basically, GCC mindlessly relies on +// obscure details in the C++ standard that make reinterpret_cast +// virtually useless. +template +inline D bit_copy(const S& source) { + D destination; + // This use of memcpy is safe: source and destination cannot overlap. + memcpy(&destination, &source, sizeof(destination)); + return destination; +} + +// A macro to ensure that memcpy cannot be called. memcpy does not handle +// overlapping memory regions. Even though this is well documented it seems +// to be used in error quite often. To avoid problems we disallow the direct +// use of memcpy here. +// +// On Windows the basic libraries use memcpy and therefore compilation will +// fail if memcpy is overwritten even if user code does not use memcpy. +#if defined(memcpy) +#undef memcpy +#endif +#if !defined(TARGET_OS_WINDOWS) +#define memcpy "Please use memmove instead of memcpy." +#endif + + +// When using GCC we can use GCC attributes to ensure that certain +// contants are 16 byte aligned. +#if defined(TARGET_OS_WINDOWS) +#define ALIGN16 __declspec(align(16)) +#else +#define ALIGN16 __attribute__((aligned(16))) +#endif + +} // namespace dart + +#endif // VM_GLOBALS_H_ diff --git a/runtime/vm/growable_array.h b/runtime/vm/growable_array.h new file mode 100644 index 00000000000..1175b868a41 --- /dev/null +++ b/runtime/vm/growable_array.h @@ -0,0 +1,133 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Defines growable array classes, that differ where they are allocated: +// - GrowableArray: allocate on stack. +// - ZoneGrowableArray: allocated in the zone. + +#ifndef VM_GROWABLE_ARRAY_H_ +#define VM_GROWABLE_ARRAY_H_ + +#include "vm/allocation.h" +#include "vm/isolate.h" +#include "vm/utils.h" +#include "vm/zone.h" + +namespace dart { + +template +class BaseGrowableArray : public B { + public: + BaseGrowableArray() : length_(0), capacity_(0), data_(NULL), zone_(NULL) { + ASSERT(Isolate::Current() != NULL); + zone_ = Isolate::Current()->current_zone(); + } + + explicit BaseGrowableArray(int initial_capacity) + : length_(0), capacity_(0), data_(NULL), zone_(NULL) { + ASSERT(Isolate::Current() != NULL); + zone_ = Isolate::Current()->current_zone(); + if (initial_capacity > 0) { + capacity_ = Utils::RoundUpToPowerOfTwo(initial_capacity); + data_ = reinterpret_cast(zone_->Allocate(capacity_ * sizeof(T))); + } + } + + int length() const { return length_; } + T* data() const { return data_; } + bool is_empty() const { return length_ == 0; } + + void Add(const T& value) { + Resize(length() + 1); + Last() = value; + } + + void RemoveLast() { + ASSERT(length_ > 0); + length_--; + } + + T& operator[](int index) const { + ASSERT(0 <= index); + ASSERT(index < length_); + ASSERT(length_ <= capacity_); + return data_[index]; + } + + T& Last() const { + ASSERT(length_ > 0); + return operator[](length_ - 1); + } + + void AddArray(const BaseGrowableArray& src) { + for (int i = 0; i < src.length(); i++) { + Add(src[i]); + } + } + + void Clear() { + length_ = 0; + } + + // Sort the array in place. + inline void Sort(int compare(const T*, const T*)); + + private: + int length_; + int capacity_; + T* data_; + Zone* zone_; // Zone in which we are allocating the array. + + void Resize(int new_length); + + DISALLOW_COPY_AND_ASSIGN(BaseGrowableArray); +}; + + +template +inline void BaseGrowableArray::Sort( + int compare(const T*, const T*)) { + typedef int (*CompareFunction)(const void*, const void*); + qsort(data_, length_, sizeof(T), reinterpret_cast(compare)); +} + + +template +void BaseGrowableArray::Resize(int new_length) { + if (new_length > capacity_) { + ASSERT(Isolate::Current() != NULL); + // Check that we allocating in the array's zone. + ASSERT(zone_ == Isolate::Current()->current_zone()); + int new_capacity = Utils::RoundUpToPowerOfTwo(new_length); + T* new_data = reinterpret_cast( + zone_->Reallocate(reinterpret_cast(data_), + capacity_ * sizeof(T), + new_capacity * sizeof(T))); + ASSERT(new_data != NULL); + data_ = new_data; + capacity_ = new_capacity; + } + length_ = new_length; +} + + +template +class GrowableArray : public BaseGrowableArray { + public: + explicit GrowableArray(int initial_capacity) + : BaseGrowableArray(initial_capacity) {} + GrowableArray() : BaseGrowableArray() {} +}; + + +template +class ZoneGrowableArray : public BaseGrowableArray { + public: + explicit ZoneGrowableArray(int initial_capacity) + : BaseGrowableArray(initial_capacity) {} + ZoneGrowableArray() : BaseGrowableArray() {} +}; + +} // namespace dart + +#endif // VM_GROWABLE_ARRAY_H_ diff --git a/runtime/vm/growable_array_test.cc b/runtime/vm/growable_array_test.cc new file mode 100644 index 00000000000..c5527269332 --- /dev/null +++ b/runtime/vm/growable_array_test.cc @@ -0,0 +1,86 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/growable_array.h" +#include "vm/unit_test.h" + +namespace dart { + +TEST_CASE(GrowableArray) { + GrowableArray g; + EXPECT_EQ(0, g.length()); + EXPECT(g.is_empty()); + g.Add(5); + EXPECT_EQ(5, g[0]); + EXPECT_EQ(1, g.length()); + EXPECT(!g.is_empty()); + g.Add(3); + const GrowableArray& temp = g; + EXPECT_EQ(5, temp[0]); + EXPECT_EQ(3, temp[1]); + for (int i = 0; i < 10000; i++) { + g.Add(i); + } + EXPECT_EQ(10002, g.length()); + EXPECT_EQ(10000 - 1, g.Last()); + + GrowableArray f(10); + EXPECT_EQ(0, f.length()); + f.Add(-1LL); + f.Add(15LL); + EXPECT_EQ(2, f.length()); + for (int64_t l = 0; l < 100; l++) { + f.Add(l); + } + EXPECT_EQ(102, f.length()); + EXPECT_EQ(100 - 1, f.Last()); + EXPECT_EQ(-1LL, f[0]); + + GrowableArray h; + EXPECT_EQ(0, h.length()); + h.Add(101); + h.Add(102); + h.Add(103); + EXPECT_EQ(3, h.length()); + EXPECT_EQ(103, h.Last()); + h.RemoveLast(); + EXPECT_EQ(2, h.length()); + EXPECT_EQ(102, h.Last()); + h.RemoveLast(); + EXPECT_EQ(1, h.length()); + EXPECT_EQ(101, h.Last()); + h.RemoveLast(); + EXPECT_EQ(0, h.length()); + EXPECT(h.is_empty()); + h.Add(-8899); + h.Add(7908); + EXPECT(!h.is_empty()); + h.Clear(); + EXPECT(h.is_empty()); +} + + +static int greatestFirst(const int* a, const int* b) { + if (*a > *b) { + return -1; + } else if (*a < *b) { + return 1; + } else { + return 0; + } +} + +TEST_CASE(GrowableArraySort) { + GrowableArray g; + g.Add(12); + g.Add(4); + g.Add(64); + g.Add(8); + g.Sort(greatestFirst); + EXPECT_EQ(64, g[0]); + EXPECT_EQ(4, g.Last()); +} + +} // namespace dart diff --git a/runtime/vm/handles.cc b/runtime/vm/handles.cc new file mode 100644 index 00000000000..db83bc1d5bd --- /dev/null +++ b/runtime/vm/handles.cc @@ -0,0 +1,115 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/handles.h" + +#include "vm/assert.h" +#include "vm/flags.h" +#include "vm/isolate.h" +#include "vm/os.h" +#include "vm/raw_object.h" +#include "vm/utils.h" +#include "vm/visitor.h" +#include "vm/zone.h" + +#include "vm/handles_impl.h" + +namespace dart { + +DEFINE_DEBUG_FLAG(bool, trace_handles_count, + false, "Trace count of handles allocated."); + + +VMHandles::~VMHandles() { +} + + +void VMHandles::VisitObjectPointers(ObjectPointerVisitor* visitor) { + return Handles::VisitObjectPointers(visitor); +} + + +uword VMHandles::AllocateHandle() { + return Handles::AllocateHandle(); +} + + +uword VMHandles::AllocateZoneHandle() { + return Handles::AllocateZoneHandle(); +} + + +bool VMHandles::IsZoneHandle(uword handle) { + return Handles::IsZoneHandle(handle); +} + + +int VMHandles::ScopedHandleCount() { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate->current_zone() != NULL); + VMHandles* handles = isolate->current_zone()->handles(); + return handles->CountScopedHandles(); +} + + +int VMHandles::ZoneHandleCount() { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate->current_zone() != NULL); + VMHandles* handles = isolate->current_zone()->handles(); + return handles->CountZoneHandles(); +} + + +HandleScope::HandleScope() : StackResource() { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate->no_handle_scope_depth() == 0); + VMHandles* handles = isolate->current_zone()->handles(); + ASSERT(handles != NULL); + saved_handle_block_ = handles->scoped_blocks_; + saved_handle_slot_ = handles->scoped_blocks_->next_handle_slot(); +#if defined(DEBUG) + link_ = isolate->top_handle_scope(); + isolate->set_top_handle_scope(this); +#endif +} + + +HandleScope::~HandleScope() { + Isolate* isolate = Isolate::Current(); + ASSERT(isolate->current_zone() != NULL); + VMHandles* handles = isolate->current_zone()->handles(); + ASSERT(handles != NULL); + handles->scoped_blocks_ = saved_handle_block_; + handles->scoped_blocks_->set_next_handle_slot(saved_handle_slot_); +#if defined(DEBUG) + handles->VerifyScopedHandleState(); + handles->ZapFreeScopedHandles(); + ASSERT(isolate->top_handle_scope() == this); + isolate->set_top_handle_scope(link_); +#endif +} + + +#if defined(DEBUG) +NoHandleScope::NoHandleScope() : StackResource() { + Isolate* isolate = Isolate::Current(); + isolate->IncrementNoHandleScopeDepth(); +} + + +NoHandleScope::~NoHandleScope() { + Isolate* isolate = Isolate::Current(); + isolate->DecrementNoHandleScopeDepth(); +} +#endif // defined(DEBUG) + +} // namespace dart diff --git a/runtime/vm/handles.h b/runtime/vm/handles.h new file mode 100644 index 00000000000..c8fca0dd96c --- /dev/null +++ b/runtime/vm/handles.h @@ -0,0 +1,307 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_HANDLES_H_ +#define VM_HANDLES_H_ + +#include "vm/allocation.h" + +namespace dart { + +// Handles are used in the Dart Virtual Machine to ensure that access +// to dart objects in the virtual machine code is done in a +// Garbage Collection safe manner. +// +// The class Handles is the basic type that implements creation of handles and +// manages their life cycle (allocated either in the current zone or +// current handle scope). +// The two forms of handle allocation are: +// - allocation of handles in the current zone (Handle::AllocateZoneHandle). +// Handles allocated in this manner are destroyed when the zone is destroyed. +// - allocation of handles in a scoped manner (Handle::AllocateHandle). +// A new scope can be started using HANDLESCOPE(). +// Handles allocated in this manner are destroyed when the HandleScope +// object is destroyed. +// Code that uses scoped handles typically looks as follows: +// { +// HANDLESCOPE(); +// const String& str = String::Handle(String::New("abc")); +// ..... +// ..... +// } +// Code that uses zone handles typically looks as follows: +// const String& str = String::ZoneHandle(String::New("abc")); +// ..... +// ..... +// +// The Handle function for each object type internally uses the +// Handles::AllocateHandle() function for creating handles. The Handle +// function of the object type is the only way to create scoped handles +// in the dart VM. +// The ZoneHandle function for each object type internally uses the +// Handles::AllocateZoneHandle() function for creating zone handles. +// The ZoneHandle function of the object type is the only way to create +// zone handles in the dart VM. +// +// There are some critical regions of the Dart VM were we may need to manipulate +// raw dart objects directly. We use NOHANDLESCOPE to assert that we do not +// add code that will allocate new handles during this critical area. +// { +// NOHANDLESCOPE(); +// .... +// .... +// } + + +// Forward declarations. +class ObjectPointerVisitor; + + +template +class Handles { + public: + Handles() + : zone_blocks_(NULL), + first_scoped_block_(NULL), + scoped_blocks_(&first_scoped_block_) { + } + ~Handles() { + DeleteAll(); + } + + // Visit all object pointers stored in the various handles. + void VisitObjectPointers(ObjectPointerVisitor* visitor); + + // Allocates a handle in the current handle scope. This handle is valid only + // in the current handle scope and is destroyed when the current handle + // scope ends. + static uword AllocateHandle(); + + // Allocates a handle in the current zone. This handle will be destroyed + // when the current zone is destroyed. + static uword AllocateZoneHandle(); + + // Returns true if specified handle is a zone handle. + static bool IsZoneHandle(uword handle); + + protected: + // Allocates space for a scoped handle. + uword AllocateScopedHandle() { + if (scoped_blocks_->IsFull()) { + SetupNextScopeBlock(); + } + return scoped_blocks_->AllocateHandle(); + } + + // Returns a count of active handles (used for testing purposes). + int CountScopedHandles() const; + int CountZoneHandles() const; + + // Returns true if passed in handle is a valid zone handle. + bool IsValidScopedHandle(uword handle) const; + bool IsValidZoneHandle(uword handle) const; + + private: + // Base structure for managing blocks of handles. + // Handles are allocated in Chunks (each chunk holds kHandlesPerChunk + // handles). The chunk is uninitialized, subsequent requests for handles + // is allocated from the chunk until we run out space in the chunk, + // at this point another chunk is allocated. These chunks are chained + // together. + class HandlesBlock { + public: + explicit HandlesBlock(HandlesBlock* next) + : next_handle_slot_(0), + next_block_(next) { } + ~HandlesBlock(); + + // Reinitializes handle block for reuse. + void ReInit(); + + // Returns true if the handle block is full. + bool IsFull() const { + return next_handle_slot_ >= (kHandleSizeInWords * kHandlesPerChunk); + } + + // Returns true if passed in handle belongs to this block. + bool IsValidHandle(uword handle) const { + uword start = reinterpret_cast(data_); + uword end = start + (kHandleSizeInWords * kWordSize * kHandlesPerChunk); + return (start <= handle && handle < end); + } + + // Allocates space for a handle in the data area. + uword AllocateHandle() { + ASSERT(!IsFull()); + uword handle_address = reinterpret_cast(data_ + next_handle_slot_); + next_handle_slot_ += kHandleSizeInWords; + return handle_address; + } + + // Visit all object pointers in the handle block. + void VisitObjectPointers(ObjectPointerVisitor* visitor); + +#if defined(DEBUG) + // Zaps the free handle area to an uninitialized value. + void ZapFreeHandles(); +#endif + + // Returns number of active handles in the handle block. + int HandleCount() const; + + // Accessors. + intptr_t next_handle_slot() const { return next_handle_slot_; } + void set_next_handle_slot(intptr_t next_handle_slot) { + next_handle_slot_ = next_handle_slot; + } + HandlesBlock* next_block() const { return next_block_; } + void set_next_block(HandlesBlock* next) { next_block_ = next; } + + private: + // Zap value used to indicate uninitialized handle area (debug purposes). + static const uword kZapUninitializedWord = 0xabababab; + + uword data_[kHandleSizeInWords * kHandlesPerChunk]; // Handles area. + intptr_t next_handle_slot_; // Next slot for allocation in current block. + HandlesBlock* next_block_; // Link to next block of handles. + + DISALLOW_COPY_AND_ASSIGN(HandlesBlock); + }; + + // Deletes all the allocated handle blocks. + void DeleteAll(); + void DeleteHandleBlocks(HandlesBlock* blocks); + + // Sets up the next handle block (allocates a new one if needed). + void SetupNextScopeBlock(); + + // Allocates space for a zone handle. + uword AllocateHandleInZone() { + if (zone_blocks_ == NULL || zone_blocks_->IsFull()) { + SetupNextZoneBlock(); + } + return zone_blocks_->AllocateHandle(); + } + + // Allocates a new handle block and links it up. + void SetupNextZoneBlock(); + +#if defined(DEBUG) + // Verifies consistency of handle blocks after a scope is destroyed. + void VerifyScopedHandleState(); + + // Zaps the free scoped handles to an uninitialized value. + void ZapFreeScopedHandles(); +#endif + + HandlesBlock* zone_blocks_; // List of zone handles. + HandlesBlock first_scoped_block_; // First block of scoped handles. + HandlesBlock* scoped_blocks_; // List of scoped handles. + + friend class HandleScope; + DISALLOW_ALLOCATION(); + DISALLOW_COPY_AND_ASSIGN(Handles); +}; + + +static const int kVMHandleSizeInWords = 2; +static const int kVMHandlesPerChunk = 64; +static const int kOffsetOfRawPtr = kWordSize; +class VMHandles : public Handles { + public: + static const int kOffsetOfRawPtrInHandle = kOffsetOfRawPtr; + + VMHandles() : Handles() { } + ~VMHandles(); + + // Visit all object pointers stored in the various handles. + void VisitObjectPointers(ObjectPointerVisitor* visitor); + + // Allocates a handle in the current handle scope. This handle is valid only + // in the current handle scope and is destroyed when the current handle + // scope ends. + static uword AllocateHandle(); + + // Allocates a handle in the current zone. This handle will be destroyed + // when the current zone is destroyed. + static uword AllocateZoneHandle(); + + // Returns true if specified handle is a zone handle. + static bool IsZoneHandle(uword handle); + + // Returns number of handles, these functions are used for testing purposes. + static int ScopedHandleCount(); + static int ZoneHandleCount(); +}; + + +// The class HandleScope is used to start a new handles scope in the code. +// It is used as follows: +// { +// HANDLESCOPE(); +// .... +// ..... +// code that creates some scoped handles. +// .... +// } +class HandleScope : public StackResource { + public: + HandleScope(); + ~HandleScope(); + + private: + VMHandles::HandlesBlock* saved_handle_block_; // Handle block at prev scope. + uword saved_handle_slot_; // Next available handle slot at previous scope. +#if defined(DEBUG) + HandleScope* link_; // Link to previous scope. +#endif + DISALLOW_COPY_AND_ASSIGN(HandleScope); +}; + +// Macro to start a new Handle scope. +#define HANDLESCOPE() dart::HandleScope vm_internal_handles_scope_; + + +// The class NoHandleScope is used in critical regions of the virtual machine +// code where raw dart object pointers are directly manipulated. +// This class asserts that we do not add code that will allocate new handles +// during this critical area. +// It is used as follows: +// { +// NOHANDLESCOPE(); +// .... +// ..... +// critical code that manipulates dart objects directly. +// .... +// } +#if defined(DEBUG) +class NoHandleScope : public StackResource { + public: + NoHandleScope(); + ~NoHandleScope(); + + private: + DISALLOW_COPY_AND_ASSIGN(NoHandleScope); +}; +#else // defined(DEBUG) +class NoHandleScope : public ValueObject { + public: + NoHandleScope() { } + ~NoHandleScope() { } + + private: + DISALLOW_COPY_AND_ASSIGN(NoHandleScope); +}; +#endif // defined(DEBUG) + +// Macro to start a no handles scope in the code. +#define NOHANDLESCOPE() dart::NoHandleScope no_vm_internal_handles_scope_; + +} // namespace dart + +#endif // VM_HANDLES_H_ diff --git a/runtime/vm/handles_impl.h b/runtime/vm/handles_impl.h new file mode 100644 index 00000000000..64abd942c0a --- /dev/null +++ b/runtime/vm/handles_impl.h @@ -0,0 +1,310 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_HANDLES_IMPL_H_ +#define VM_HANDLES_IMPL_H_ + +namespace dart { + +DECLARE_DEBUG_FLAG(bool, trace_handles_count); + +template +void Handles::VisitObjectPointers( + ObjectPointerVisitor* visitor) { + // Visit all zone handles. + HandlesBlock* block = zone_blocks_; + while (block != NULL) { + block->VisitObjectPointers(visitor); + block = block->next_block(); + } + + // Visit all scoped handles. + block = &first_scoped_block_; + do { + block->VisitObjectPointers(visitor); + block = block->next_block(); + } while (block != NULL); +} + + +// Figure out the current handle scope using the current Isolate and +// allocate a handle in that scope. The function assumes that a +// current Isolate, current zone and current handle scope exist. It +// asserts for this appropriately. +template +uword Handles::AllocateHandle() { + // TODO(5411412): Accessing the current isolate is a performance problem, + // consider passing it down as a parameter. + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ASSERT(isolate->current_zone() != NULL); + ASSERT(isolate->top_handle_scope() != NULL); + ASSERT(isolate->no_handle_scope_depth() == 0); + Handles* handles = isolate->current_zone()->handles(); + ASSERT(handles != NULL); + return handles->AllocateScopedHandle(); +} + + +// Figure out the current zone using the current Isolate and +// allocate a handle in that zone. The function assumes that a +// current Isolate and current zone exist. It asserts for +// this appropriately. +template +uword Handles::AllocateZoneHandle() { + // TODO(5411412): Accessing the current isolate is a performance problem, + // consider passing it down as a parameter. + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ASSERT(isolate->current_zone() != NULL); + ASSERT(isolate->no_handle_scope_depth() == 0); + Handles* handles = isolate->current_zone()->handles(); + ASSERT(handles != NULL); + return handles->AllocateHandleInZone(); +} + + +// Figure out the current zone using the current Isolate and +// check if the specified handle has been allocated in this zone. +template +bool Handles::IsZoneHandle(uword handle) { + // TODO(5411412): Accessing the current isolate is a performance problem, + // consider passing it down as a parameter. + Isolate* isolate = Isolate::Current(); + ASSERT(isolate != NULL); + ASSERT(isolate->current_zone() != NULL); + Handles* handles = isolate->current_zone()->handles(); + ASSERT(handles != NULL); + return handles->IsValidZoneHandle(handle); +} + + +template +void Handles::DeleteAll() { + // Delete all the zone allocated handle blocks. + DeleteHandleBlocks(zone_blocks_); + zone_blocks_ = NULL; + + // Delete all the scoped handle blocks. + scoped_blocks_ = first_scoped_block_.next_block(); + DeleteHandleBlocks(scoped_blocks_); + first_scoped_block_.ReInit(); + scoped_blocks_ = &first_scoped_block_; +} + + +template +void Handles::DeleteHandleBlocks( + HandlesBlock* blocks) { + while (blocks != NULL) { + HandlesBlock* block = blocks; + blocks = blocks->next_block(); + delete block; + } +} + + +template +void Handles::SetupNextScopeBlock() { +#if defined(DEBUG) + if (FLAG_trace_handles_count) { + OS::Print("Handle Counts: Zone = %d, Scoped = %d\n", + CountZoneHandles(), CountScopedHandles()); + } +#endif + if (scoped_blocks_->next_block() == NULL) { + scoped_blocks_->set_next_block(new HandlesBlock(NULL)); + } + scoped_blocks_ = scoped_blocks_->next_block(); + scoped_blocks_->set_next_handle_slot(0); +#if defined(DEBUG) + scoped_blocks_->ZapFreeHandles(); +#endif +} + + +// Validation of the handle involves iterating through all the +// handle blocks to check if the handle is valid, please +// use this only in ASSERT code for verification purposes. +template +bool Handles::IsValidScopedHandle(uword handle) const { + const HandlesBlock* iterator = &first_scoped_block_; + while (iterator != NULL) { + if (iterator->IsValidHandle(handle)) { + return true; + } + iterator = iterator->next_block(); + } + return false; +} + + +template +bool Handles::IsValidZoneHandle(uword handle) const { + const HandlesBlock* iterator = zone_blocks_; + while (iterator != NULL) { + if (iterator->IsValidHandle(handle)) { + return true; + } + iterator = iterator->next_block(); + } + return false; +} + + +template +void Handles::SetupNextZoneBlock() { +#if defined(DEBUG) + if (FLAG_trace_handles_count) { + OS::Print("Handle Counts: Zone = %d, Scoped = %d\n", + CountZoneHandles(), CountScopedHandles()); + } +#endif + zone_blocks_ = new HandlesBlock(zone_blocks_); + ASSERT(zone_blocks_ != NULL); +} + + +#if defined(DEBUG) +template +void Handles::VerifyScopedHandleState() { + HandlesBlock* block = &first_scoped_block_; + const intptr_t end_index = (kHandleSizeInWords * kHandlesPerChunk); + do { + if (scoped_blocks_ == block && block->next_handle_slot() <= end_index) { + return; + } + block = block->next_block(); + } while (block != NULL); + ASSERT(false); +} + + +template +void Handles::ZapFreeScopedHandles() { + HandlesBlock* block = scoped_blocks_; + while (block != NULL) { + block->ZapFreeHandles(); + block = block->next_block(); + } +} +#endif + + +template +int Handles::CountScopedHandles() const { + int count = 0; + const HandlesBlock* block = &first_scoped_block_; + do { + count += block->HandleCount(); + if (block == scoped_blocks_) { + return count; + } + block = block->next_block(); + } while (block != NULL); + UNREACHABLE(); + return 0; +} + + +template +int Handles::CountZoneHandles() const { + int count = 0; + const HandlesBlock* block = zone_blocks_; + while (block != NULL) { + count += block->HandleCount(); + block = block->next_block(); + } + return count; +} + + +template +Handles::HandlesBlock::~HandlesBlock() { +#if defined(DEBUG) + ReInit(); +#endif +} + + +template +void Handles::HandlesBlock::ReInit() { + next_handle_slot_ = 0; + next_block_ = NULL; +#if defined(DEBUG) + ZapFreeHandles(); +#endif +} + + +template +void Handles::HandlesBlock::VisitObjectPointers( + ObjectPointerVisitor* visitor) { + ASSERT(visitor != NULL); + for (intptr_t i = 0; i < next_handle_slot_; i += kHandleSizeInWords) { + visitor->VisitPointer( + reinterpret_cast(&data_[i + kOffsetOfRawPtr/kWordSize])); + } +} + + +#if defined(DEBUG) +template +void Handles::HandlesBlock::ZapFreeHandles() { + // Reinitialize the handle area to some uninitialized value. + for (intptr_t i = next_handle_slot_; + i < (kHandleSizeInWords * kHandlesPerChunk); + i++) { + data_[i] = kZapUninitializedWord; + } +} +#endif + + +template +int Handles::HandlesBlock::HandleCount() const { + return (next_handle_slot_ / kHandleSizeInWords); +} + +} // namespace dart + +#endif // VM_HANDLES_IMPL_H_ diff --git a/runtime/vm/handles_test.cc b/runtime/vm/handles_test.cc new file mode 100644 index 00000000000..731dd036e8a --- /dev/null +++ b/runtime/vm/handles_test.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/flags.h" +#include "vm/handles.h" +#include "vm/heap.h" +#include "vm/object.h" +#include "vm/unit_test.h" +#include "vm/zone.h" + +namespace dart { + +DECLARE_DEBUG_FLAG(bool, trace_handles_count); + + +// Unit test for Zone handle allocation. +TEST_CASE(AllocateZoneHandle) { +#if defined(DEBUG) + FLAG_trace_handles_count = true; +#endif + // The previously run stub code generation may have created zone handles. + int initial_count = VMHandles::ZoneHandleCount(); + static const int kNumHandles = 65; + // Create some zone handles. + for (int i = 0; i < kNumHandles; i++) { + const Smi& handle = Smi::ZoneHandle(Smi::New(i)); + EXPECT(handle.IsSmi()); + EXPECT_EQ(i, handle.Value()); + } + EXPECT_EQ(kNumHandles + initial_count, VMHandles::ZoneHandleCount()); + // Create some more zone handles. + for (int i = kNumHandles; i < (2 * kNumHandles); i++) { + const Smi& handle = Smi::ZoneHandle(Smi::New(i)); + EXPECT(handle.IsSmi()); + EXPECT_EQ(i, handle.Value()); + } + EXPECT_EQ((2 * kNumHandles) + initial_count, VMHandles::ZoneHandleCount()); +} + + +// Unit test for Scope handle allocation. +TEST_CASE(AllocateScopeHandle) { +#if defined(DEBUG) + FLAG_trace_handles_count = true; +#endif + int32_t handle_count = VMHandles::ScopedHandleCount(); + static const int kNumHandles = 65; + // Create some scoped handles. + { + HANDLESCOPE(); + for (int i = 0; i < kNumHandles; i++) { + const Smi& handle = Smi::Handle(Smi::New(i)); + EXPECT(handle.IsSmi()); + EXPECT_EQ(i, handle.Value()); + } + EXPECT_EQ((handle_count + kNumHandles), VMHandles::ScopedHandleCount()); + // Create lots of scoped handles in a loop with a nested scope. + for (int loop = 0; loop < 1000; loop++) { + HANDLESCOPE(); + for (int i = 0; i < 2; i++) { + const Smi& handle = Smi::Handle(Smi::New(i + loop)); + EXPECT(handle.IsSmi()); + EXPECT_EQ(i + loop, handle.Value()); + } + EXPECT_EQ((handle_count + kNumHandles + 2), + VMHandles::ScopedHandleCount()); + } + EXPECT_EQ((handle_count + kNumHandles), VMHandles::ScopedHandleCount()); + for (int i = 0; i < kNumHandles; i++) { + const Smi& handle = Smi::Handle(Smi::New(i)); + EXPECT(handle.IsSmi()); + EXPECT_EQ(i, handle.Value()); + } + EXPECT_EQ((handle_count + (2 * kNumHandles)), + VMHandles::ScopedHandleCount()); + } + EXPECT_EQ(handle_count, VMHandles::ScopedHandleCount()); +} + +} // namespace dart diff --git a/runtime/vm/heap.cc b/runtime/vm/heap.cc new file mode 100644 index 00000000000..d90507dc1e8 --- /dev/null +++ b/runtime/vm/heap.cc @@ -0,0 +1,140 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/heap.h" + +#include "vm/assert.h" +#include "vm/compiler_stats.h" +#include "vm/flags.h" +#include "vm/isolate.h" +#include "vm/os.h" +#include "vm/pages.h" +#include "vm/scavenger.h" +#include "vm/utils.h" +#include "vm/verifier.h" +#include "vm/virtual_memory.h" + +namespace dart { + +DEFINE_FLAG(bool, verbose_gc, false, "Enables verbose GC."); +DEFINE_FLAG(bool, gc_at_alloc, false, "GC at every allocation."); + +Heap::Heap() { + new_space_ = new Scavenger(this, 32 * MB, kNewObjectAlignmentOffset); + old_space_ = new PageSpace(this, kHeapSize); + code_space_ = new PageSpace(this, kCodeHeapSize, true); +} + + +Heap::~Heap() { + delete new_space_; + delete old_space_; + delete code_space_; +} + + +uword Heap::AllocateNew(intptr_t size) { + ASSERT(Isolate::Current()->no_gc_scope_depth() == 0); + uword addr = new_space_->TryAllocate(size); + if (addr != 0) { + return addr; + } + new_space_->Scavenge(); + if (FLAG_verbose_gc) { + OS::PrintErr("New space (%dk) Old space (%dk) Code space (%dk)\n", + (new_space_->in_use() / KB), + (old_space_->in_use() / KB), + (code_space_->in_use() / KB)); + } + addr = new_space_->TryAllocate(size); + if (addr != 0) { + return addr; + } + return AllocateOld(size); +} + + +uword Heap::AllocateOld(intptr_t size) { + ASSERT(Isolate::Current()->no_gc_scope_depth() == 0); + uword addr = old_space_->TryAllocate(size); + if (addr == 0) { + // TODO(iposva): Support GC. + FATAL("Exhausted heap space."); + } + return addr; +} + + +uword Heap::AllocateCode(intptr_t size) { + ASSERT(Isolate::Current()->no_gc_scope_depth() == 0); + ASSERT(Utils::IsAligned(size, OS::PreferredCodeAlignment())); + uword addr = code_space_->TryAllocate(size); + if (addr == 0) { + // TODO(iposva): Support GC. + FATAL("Exhausted code heap space."); + } + if (FLAG_compiler_stats) { + CompilerStats::code_allocated += size; + } + return addr; +} + + +bool Heap::Contains(uword addr) const { + return new_space_->Contains(addr) || + old_space_->Contains(addr) || + code_space_->Contains(addr); +} + + +bool Heap::CodeContains(uword addr) const { + return code_space_->Contains(addr); +} + + +void Heap::Init(Isolate* isolate) { + ASSERT(isolate->heap() == NULL); + Heap* heap = new Heap(); + isolate->set_heap(heap); +} + + +bool Heap::Verify() const { + VerifyPointersVisitor visitor; + new_space_->VisitObjectPointers(&visitor); + old_space_->VisitObjectPointers(&visitor); + code_space_->VisitObjectPointers(&visitor); + // Only returning a value so that Heap::Validate can be called from an ASSERT. + return true; +} + + +void Heap::IterateOldPointers(ObjectPointerVisitor* visitor) { + old_space_->VisitObjectPointers(visitor); + code_space_->VisitObjectPointers(visitor); +} + + +uword Heap::TopAddress() { + return reinterpret_cast(new_space_->TopAddress()); +} + + +uword Heap::EndAddress() { + return reinterpret_cast(new_space_->EndAddress()); +} + + +#if defined(DEBUG) +NoGCScope::NoGCScope() : StackResource(), isolate_(Isolate::Current()) { + isolate_->IncrementNoGCScopeDepth(); +} + + +NoGCScope::~NoGCScope() { + isolate_->DecrementNoGCScopeDepth(); +} +#endif // defined(DEBUG) + +} // namespace dart diff --git a/runtime/vm/heap.h b/runtime/vm/heap.h new file mode 100644 index 00000000000..bd56f3697e4 --- /dev/null +++ b/runtime/vm/heap.h @@ -0,0 +1,106 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_HEAP_H_ +#define VM_HEAP_H_ + +#include "vm/allocation.h" +#include "vm/flags.h" +#include "vm/globals.h" + +namespace dart { + +// Forward declarations. +class Isolate; +class ObjectPointerVisitor; +class PageSpace; +class Scavenger; +class VirtualMemory; + +DECLARE_FLAG(bool, verbose_gc); +DECLARE_FLAG(bool, gc_at_alloc); + +class Heap { + public: + enum Space { + kNew, + kOld, + kExecutable + }; + + ~Heap(); + + uword Allocate(intptr_t size, Space space) { + switch (space) { + case kNew: + return AllocateNew(size); + case kOld: + return AllocateOld(size); + case kExecutable: + return AllocateCode(size); + default: + UNREACHABLE(); + } + return 0; + } + + // Heap contains the specified address. + bool Contains(uword addr) const; + bool CodeContains(uword addr) const; + + // Initialize the heap and register it with the isolate. + static void Init(Isolate* isolate); + + // Verify that all pointers in the heap point to the heap. + bool Verify() const; + + void IterateOldPointers(ObjectPointerVisitor* visitor); + + // Accessors for inlined allocation in generated code. + uword TopAddress(); + uword EndAddress(); + static intptr_t new_space_offset() { return OFFSET_OF(Heap, new_space_); } + + private: + Heap(); + + uword AllocateNew(intptr_t size); + uword AllocateOld(intptr_t size); + uword AllocateCode(intptr_t size); + + // Allocation is limited to the below sizes. + static const intptr_t kHeapSize = 512 * MB; + static const intptr_t kCodeHeapSize = 4 * MB; + + // The different spaces used for allocation. + Scavenger* new_space_; + PageSpace* old_space_; + PageSpace* code_space_; + + DISALLOW_COPY_AND_ASSIGN(Heap); +}; + + +#if defined(DEBUG) +class NoGCScope : public StackResource { + public: + NoGCScope(); + ~NoGCScope(); + private: + Isolate* isolate_; + + DISALLOW_COPY_AND_ASSIGN(NoGCScope); +}; +#else // defined(DEBUG) +class NoGCScope : public ValueObject { + public: + NoGCScope() {} + private: + DISALLOW_COPY_AND_ASSIGN(NoGCScope); +}; +#endif // defined(DEBUG) + +} // namespace dart + +#endif // VM_HEAP_H_ diff --git a/runtime/vm/heap_test.cc b/runtime/vm/heap_test.cc new file mode 100644 index 00000000000..cba6e43f61c --- /dev/null +++ b/runtime/vm/heap_test.cc @@ -0,0 +1,5 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(iposva): Intentionally left blank for now. diff --git a/runtime/vm/ic_stubs.cc b/runtime/vm/ic_stubs.cc new file mode 100644 index 00000000000..65d1624c4be --- /dev/null +++ b/runtime/vm/ic_stubs.cc @@ -0,0 +1,137 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/ic_stubs.h" + +#include "vm/object.h" + +namespace dart { + +ICData::ICData(const Code& ic_stub) : ic_stub_(ic_stub) { + ASSERT(!ic_stub_.IsNull()); +} + + +intptr_t ICData::NumberOfClasses() const { + const Array& data = Array::Handle(ic_stub_.ic_data()); + Smi& smi = Smi::Handle(); + smi ^= data.At(0); + return smi.Value(); +} + + +intptr_t ICData::NumberOfChecks() const { + intptr_t number_of_classes = NumberOfClasses(); + const Array& data = Array::Handle(ic_stub_.ic_data()); + intptr_t length = data.Length(); + // First element is number of classes (N), followed by a group of elements, + // each element consisting of N classes + 1 target. + return (length - 1) / (number_of_classes + 1); +} + + +void ICData::GetCheckAt(intptr_t index, + GrowableArray* classes, + Function* target) const { + ASSERT(classes != NULL); + ASSERT(target != NULL); + ASSERT((0 <= index) && (index < NumberOfChecks())); + classes->Clear(); + const Array& data = Array::Handle(ic_stub_.ic_data()); + intptr_t num_classes = NumberOfClasses(); + intptr_t pos = 1 + (num_classes + 1) * index; + for (intptr_t i = 0; i < num_classes; i++) { + Class& cls = Class::ZoneHandle(); + cls ^= data.At(pos++); + classes->Add(&cls); + } + (*target) ^= data.At(pos); +} + + +void ICData::SetCheckAt(intptr_t index, + const GrowableArray& classes, + const Function& target) { + ASSERT((0 <= index) && (index < NumberOfChecks())); + const Array& data = Array::Handle(ic_stub_.ic_data()); + intptr_t num_classes = NumberOfClasses(); + intptr_t pos = 1 + (num_classes + 1) * index; + for (intptr_t i = 0; i < num_classes; i++) { + data.SetAt(pos++, *(classes[i])); + } + data.SetAt(pos, target); +} + + +void ICData::SetICDataArray(intptr_t num_classes, intptr_t num_checks) { + intptr_t len = 1 + (num_classes + 1) * num_checks; + const Array& ic_data = Array::Handle(Array::New(len)); + ic_data.SetAt(0, Smi::Handle(Smi::New(num_classes))); + ic_stub_.set_ic_data(ic_data); +} + + +void ICData::Print() { + intptr_t number_of_checks = NumberOfChecks(); + GrowableArray temp_classes; + Function& temp_target = Function::Handle(); + for (intptr_t i = 0; i < number_of_checks; i++) { + GetCheckAt(i, &temp_classes, &temp_target); + for (intptr_t k = 0; k < temp_classes.length(); k++) { + OS::Print(" %d. %s\n", k, temp_classes[k]->ToCString()); + } + OS::Print("=> %s\n", temp_target.ToCString()); + } +} + + +void ICData::ChangeTargets(const Function& from, const Function& to) { + if (from.raw() == to.raw()) { + return; + } + intptr_t n = NumberOfChecks(); + GrowableArray temp_classes; + Function& temp_function = Function::Handle(); + for (int i = 0; i < n; i++) { + GetCheckAt(i, &temp_classes, &temp_function); + if (temp_function.raw() == from.raw()) { + SetCheckAt(i, temp_classes, to); + } + } +} + + +void ICData::CheckIsSame(const GrowableArray* classes, + const GrowableArray* targets) const { + ASSERT(NumberOfClasses() == 1); // Test only for 1-class checks. + intptr_t number_of_checks = NumberOfChecks(); + ASSERT((classes == NULL) || (classes->length() == number_of_checks)); + ASSERT((targets == NULL) || (targets->length() == number_of_checks)); + if (classes != NULL) { + GrowableArray ic_data_classes; + GrowableArray ic_data_targets; + GrowableArray temp_classes; + Function& temp_target = Function::Handle(); + for (intptr_t i = 0; i < number_of_checks; i++) { + GetCheckAt(i, &temp_classes, &temp_target); + ASSERT(temp_classes.length() == 1); + const Class* cls = temp_classes[0]; + intptr_t found_at = -1; + for (intptr_t k = 0; k < classes->length(); k++) { + // Check that all classes exist. + if ((*classes)[k]->raw() == cls->raw()) { + found_at = k; + break; + } + } + ASSERT(found_at != -1); + if ((*targets)[found_at]->raw() != temp_target.raw()) { + UNREACHABLE(); + } + } + } +} + +} // namespace dart + diff --git a/runtime/vm/ic_stubs.h b/runtime/vm/ic_stubs.h new file mode 100644 index 00000000000..2d291d244c9 --- /dev/null +++ b/runtime/vm/ic_stubs.h @@ -0,0 +1,114 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Class for handling inline cache stubs +// +// The initial target of an instance call is the resolving and patching runtime +// function 'ResolvePatchInstanceCall'. It resolves and compiles the +// target function and patches the instance call to jump to it +// via an inline cache stub. +// The inline cache stub checks receiver's class for a distinct set of classes +// and jumps to the appropriate target. +// An inline-cache-miss occurs if none of the classes match. As a consequence +// the old IC stub is replaced with a new one that adds the class check +// and target for the most recently seen receiver. + +#ifndef VM_IC_STUBS_H_ +#define VM_IC_STUBS_H_ + +#include "vm/allocation.h" +#include "vm/growable_array.h" + +namespace dart { + +// Forward declarations. +class Class; +class Code; +class Function; +class RawCode; + +// Class that interprets the array stored in ICData::ic_data_. +// The array format is: +// - number of arguments checked, i.e., number of classes in each check -> N. +// - group of checks, each check containing: +// - N classes. +// - 1 target/function. +// Whenever first N arguments have the same class, jump to the matching +// function/target. +class ICData : public ValueObject { + public: + explicit ICData(const Code& ic_stub); + + intptr_t NumberOfClasses() const; + intptr_t NumberOfChecks() const; + + // 'index' is 0..NumberOfChecks-1. + void GetCheckAt(intptr_t index, + GrowableArray* classes, + Function* target) const; + void SetCheckAt(intptr_t index, + const GrowableArray& classes, + const Function& target); + + // Changes all 'from' targets to 'to' targets. + void ChangeTargets(const Function& from, const Function& to); + + // Create and set an ic_data array in ic_stub_. + // Use 'SetCheckAt' to populate the array. + void SetICDataArray(intptr_t num_classes, intptr_t num_checks); + + void Print(); + + // Temporary helper method to check that the existing inline + // cache information matches the ICData. + // TODO(srdjan): Remove once transitioned to IC data. + void CheckIsSame(const GrowableArray* classes, + const GrowableArray* targets) const; + + private: + const Code& ic_stub_; + DISALLOW_COPY_AND_ASSIGN(ICData); +}; + + +class ICStubs : public AllStatic { + public: + // Returns an IC stub that jumps to targets' entry points if the receiver + // matches a class contained in 'classes' array. + static RawCode* GetICStub(const GrowableArray& classes, + const GrowableArray& targets); + + // Identify classes and their targets contained in the IC stub. + // 'ic_entry_point' is the start of the IC stubs. 'classes' and 'targets' + // are the implemented (class, target) tuples. + // Returns false if the entry_point does not point to an IC stub. + static bool RecognizeICStub(uword ic_entry_point, + GrowableArray* classes, + GrowableArray* targets); + + // Replace all 'from' targets with 'to' targets. + static void PatchTargets(uword ic_entry_point, uword from, uword to); + + // Locate a class within the array. Return -1 if class object in 'cls' + // is not in the array. + // TODO(srdjan): Remove from ICStubs interface. + static int IndexOfClass(const GrowableArray& classes, + const Class& cls); + + private: + static RawCode* FindInCode(const Code& target, + const GrowableArray& classes); + static void AppendICStubToTargets( + const GrowableArray& targets, + const GrowableArray& classes, + const Code& ic_stub); + static bool ParseICStub(uword ic_entry_point, + GrowableArray* classes, + GrowableArray* targets, + uword from, + uword to); +}; + +} // namespace dart + +#endif // VM_IC_STUBS_H_ diff --git a/runtime/vm/ic_stubs_arm.cc b/runtime/vm/ic_stubs_arm.cc new file mode 100644 index 00000000000..7b7ebf654e6 --- /dev/null +++ b/runtime/vm/ic_stubs_arm.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM. +#if defined(TARGET_ARCH_ARM) + +#include "vm/ic_stubs.h" + +#include "vm/object.h" + +namespace dart { + +RawCode* ICStubs::GetICStub(const GrowableArray& classes, + const GrowableArray& targets) { + UNIMPLEMENTED(); + return Code::null(); +} + + +bool ICStubs::RecognizeICStub(uword entry_point, + GrowableArray* classes, + GrowableArray* targets) { + UNIMPLEMENTED(); + return false; +} + +int ICStubs::IndexOfClass(const GrowableArray& classes, + const Class& cls) { + UNIMPLEMENTED(); + return false; +} + + +void ICStubs::PatchTargets(uword ic_entry_point, uword from, uword to) { + UNIMPLEMENTED(); +} + +} // namespace dart + +#endif // defined TARGET_ARCH_ARM diff --git a/runtime/vm/ic_stubs_ia32.cc b/runtime/vm/ic_stubs_ia32.cc new file mode 100644 index 00000000000..80812b17500 --- /dev/null +++ b/runtime/vm/ic_stubs_ia32.cc @@ -0,0 +1,380 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. +#if defined(TARGET_ARCH_IA32) + +#include "vm/ic_stubs.h" + +#include "vm/assembler.h" +#include "vm/code_index_table.h" +#include "vm/disassembler.h" +#include "vm/flags.h" +#include "vm/instructions.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/stub_code.h" + +namespace dart { + +DECLARE_FLAG(bool, disassemble_stubs); +DEFINE_FLAG(bool, enable_polymorphic_ic, true, + "Enable polymorphic inline caching"); +DEFINE_FLAG(bool, trace_icstub_generation, false, + "Print every generated IC stub"); + + +// TODO(srdjan): Move FindInCode and AppendICStubToTargets into the shared file. + +// Add 'classes' and 'ic_stub' to all 'targets'. Each target's code +// (class RawCode) has an array of (classes-array, ic_stub) pairs. +void ICStubs::AppendICStubToTargets( + const GrowableArray& targets, + const GrowableArray& classes, + const Code& ic_stub) { + if (FLAG_trace_icstub_generation) { + OS::Print("Appending ICstub 0x%x to targets:\n", ic_stub.EntryPoint()); + } + Code& target = Code::Handle(); + Array& class_ic_stubs_array = Array::Handle(); + Code& test_ic_stub = Code::Handle(); + for (intptr_t i = 0; i < targets.length(); i++) { + target = Code::Handle(targets[i]->code()).raw(); + if (FLAG_trace_icstub_generation) { + OS::Print(" * code 0x%x\n", target.EntryPoint()); + } + // Do not add twice: two different classes may have the same target. + test_ic_stub = ICStubs::FindInCode(target, classes); + if (test_ic_stub.IsNull()) { + // Append one class/ic-stub pair entry. + // Grow the array by two (one pair). + class_ic_stubs_array = target.class_ic_stubs(); + intptr_t new_length = class_ic_stubs_array.Length() + 2; + class_ic_stubs_array = Array::Grow(class_ic_stubs_array, new_length); + target.set_class_ic_stubs(class_ic_stubs_array); + // Create classes array out of GrowableArray classes. + Array& a = Array::Handle(Array::New(classes.length())); + for (intptr_t i = 0; i < classes.length(); i++) { + a.SetAt(i, *classes[i]); + } + class_ic_stubs_array.SetAt(new_length - 2, a); + class_ic_stubs_array.SetAt(new_length - 1, ic_stub); + if (FLAG_trace_icstub_generation) { + OS::Print(" + icstub 0x%x\n", ic_stub.EntryPoint()); + } + } else { + if (FLAG_trace_icstub_generation) { + OS::Print(" . icstub 0x%x\n", test_ic_stub.EntryPoint()); + } + } + } +} + + +// Return true if class 'test' is contained in array 'classes'. +static bool IsClassInArray(const Class& test, + const GrowableArray& classes) { + for (intptr_t i = 0; i < classes.length(); i++) { + if (classes[i]->raw() == test.raw()) { + return true; + } + } + return false; +} + + +// Linear search for the ic stub with given 'classes'. RawCode::class_ic_stubs() +// returns an array of (classes-array, ic-stub-code) pairs. Returns +// RawCode::null() if no stub is found. +RawCode* ICStubs::FindInCode(const Code& target, + const GrowableArray& classes) { + Code& result = Code::Handle(); + if (classes.is_empty()) { + return result.raw(); // RawCode::null(). + } + Array& class_ic_stubs = Array::Handle(target.class_ic_stubs()); + const intptr_t len = class_ic_stubs.Length(); + Array& array = Array::Handle(); + Class& cls = Class::Handle(); + // Iterate over all stored IC stubs/array classes pairs until match found. + for (intptr_t i = 0; i < len; i += 2) { + // i: array of classes, i + 1: ic stub code. + array ^= class_ic_stubs.At(i); + if (array.Length() == classes.length()) { + bool classes_match = true; + for (intptr_t k = 0; k < array.Length(); k++) { + cls ^= array.At(k); + if (!IsClassInArray(cls, classes)) { + classes_match = false; + break; + } + } + if (classes_match) { + // Found matching stub. + result ^= class_ic_stubs.At(i + 1); + break; + } + } + } + // If no matching stub is found, result.raw() returns null. + return result.raw(); +} + + +int ICStubs::IndexOfClass(const GrowableArray& classes, + const Class& cls) { + for (intptr_t i = 0; i < classes.length(); i++) { + if (classes[i]->raw() == cls.raw()) { + return i; + } + } + return -1; +} + + +// An IC Stub starts with a Smi test, optionally followed by a null test +// and zero or more class tests. The "StubCode::CallInstanceFunction" +// corresponds to an IC stub without any classes or targets. +bool ICStubs::RecognizeICStub(uword ic_entry_point, + GrowableArray* classes, + GrowableArray* targets) { + if (ic_entry_point == StubCode::CallInstanceFunctionLabel().address()) { + // Unresolved instance call, no classes collected yet. + return true; + } + if (ic_entry_point == StubCode::MegamorphicLookupEntryPoint()) { + // NoSuchMethod call, no classes collected. + return true; + } + return ParseICStub(ic_entry_point, classes, targets, 0, 0); +} + + +void ICStubs::PatchTargets(uword ic_entry_point, uword from, uword to) { + bool is_ok = ParseICStub(ic_entry_point, NULL, NULL, from, to); + ASSERT(is_ok); +} + + +// Parse IC stub, collect 'classes' and 'targets' and patches +// all 'from' targets with 'to' targets. No collection occurs +// if 'classes' and 'targets' are NULL, no patching occurs if +// 'from' or 'to' is 0. +// The IC structure is defined in IcStubs::GetIcStub. +bool ICStubs::ParseICStub(uword ic_entry_point, + GrowableArray* classes, + GrowableArray* targets, + uword from, + uword to) { + uword instruction_address = ic_entry_point; + bool patch_code = (from != 0) && (to != 0); + if (classes != NULL) { + classes->Clear(); + } + if (targets != NULL) { + targets->Clear(); + } + + // Part A: Load receiver, test if Smi, jump to IC miss or hit. + ICLoadReceiver load_receiver(instruction_address); + if (!load_receiver.IsValid()) { + return false; // Not an an IC stub. + } + instruction_address += load_receiver.pattern_length_in_bytes(); + + TestEaxIsSmi test_smi(instruction_address); + // The target of the Smi test determines if the test should cause + // IC miss if successful or jump to target (IC hit). Target of IC miss is the + // stub code, target of IC success is a code object. + CodeIndexTable* ci_table = Isolate::Current()->code_index_table(); + ASSERT(ci_table != NULL); + + Instructions& inst = Instructions::Handle( + Instructions::FromEntryPoint(ic_entry_point)); + ASSERT(!inst.IsNull()); + ICData ic_data(Code::Handle(inst.code())); + if (patch_code) { + const Function& from_function = + Function::Handle(ci_table->LookupFunction(from)); + const Function& to_function = + Function::Handle(ci_table->LookupFunction(to)); + ic_data.ChangeTargets(from_function, to_function); + } + + if (!StubCode::InCallInstanceFunctionStubCode(test_smi.TargetAddress())) { + // Jump is an IC success. + if (patch_code && (test_smi.TargetAddress() == from)) { + test_smi.SetTargetAddress(to); + } + const Class& smi_class = + Class::ZoneHandle(Isolate::Current()->object_store()->smi_class()); + const Code& smi_code = + Code::Handle(ci_table->LookupCode(test_smi.TargetAddress())); + ASSERT(!smi_class.IsNullClass()); + ASSERT(!smi_code.IsNull()); + if (classes != NULL) { + classes->Add(&smi_class); + } + if (targets != NULL) { + targets->Add(&Function::ZoneHandle(smi_code.function())); + } + } + instruction_address += test_smi.pattern_length_in_bytes(); + + // TODO(srdjan): Add checks that the IC stub ends with + // a null check and a jmp. + // Part B: Load receiver's class, compare with all known classes. + LoadObjectClass load_object_class(instruction_address); + if (!load_object_class.IsValid()) { + return false; + } + instruction_address += load_object_class.pattern_length_in_bytes(); + while (true) { + ICCheckReceiverClass check_class(instruction_address); + if (!check_class.IsValid()) { + // Done parsing. +#if defined(DEBUG) + // Classes can be set to NULL if we do not care about collecting them. + if (classes != NULL) { + ic_data.CheckIsSame(classes, targets); + } +#endif + return true; + } + if (patch_code && (check_class.TargetAddress() == from)) { + check_class.SetTargetAddress(to); + } + const Class& cls = Class::ZoneHandle(check_class.TestClass()); + const Code& code = Code::ZoneHandle( + ci_table->LookupCode(check_class.TargetAddress())); + ASSERT(!cls.IsNullClass()); + ASSERT(!code.IsNull()); + if (classes != NULL) { + classes->Add(&cls); + } + if (targets != NULL) { + targets->Add(&Function::ZoneHandle(code.function())); + } + instruction_address += check_class.pattern_length_in_bytes(); + } +} + + +// Generate inline cache stub for given targets and classes. +// EDX: arguments descriptor array (preserved). +// ECX: function name (unused, preserved). +// TOS: return address. +// Jump to target if the receiver's class matches the 'receiver_class'. +// Otherwise jump to megamorphic lookup. TODO(srdjan): Patch call site to go to +// megamorphic instead of going via the IC stub. +// IC stub structure: +// A: Get receiver, test if Smi, jump to IC miss or hit. +// B: Get receiver's class, compare with all known classes. +RawCode* ICStubs::GetICStub(const GrowableArray& classes, + const GrowableArray& targets) { + // Check if a matching IC stub already exists. + Code& ic_stub_code = Code::Handle(); + for (intptr_t i = 0; i < targets.length(); i++) { + ic_stub_code = + ICStubs::FindInCode(Code::Handle(targets[i]->code()), classes); + if (!ic_stub_code.IsNull()) { + return ic_stub_code.raw(); // Reusing the previously created IC stub. + } + } + + // Call IC miss handling only if polymorphic inline caching is on, otherwise + // continue in megamorphic lookup. + const ExternalLabel* ic_miss_label = + FLAG_enable_polymorphic_ic + ? &StubCode::CallInstanceFunctionLabel() + : &StubCode::MegamorphicLookupLabel(); + +#define __ assembler. + Assembler assembler; + // Part A: Get receiver, test if Smi. + // Total number of args is the first Smi in args descriptor array (EDX). + __ movl(EAX, FieldAddress(EDX, Array::data_offset())); + __ movl(EAX, Address(ESP, EAX, TIMES_2, 0)); // Get receiver. EAX is a Smi. + __ testl(EAX, Immediate(kSmiTagMask)); + + const Class& smi_class = Class::Handle( + Isolate::Current()->object_store()->smi_class()); + const int smi_class_index = IndexOfClass(classes, smi_class); + if (smi_class_index >= 0) { + // Smi is not IC miss. + const Code& target = Code::Handle(targets[smi_class_index]->code()); + ExternalLabel target_label("ICtoTargetSmi", target.EntryPoint()); + // Always check for Smi first and either go to target or call ic-miss. + __ j(ZERO, &target_label); + } else { + // Smi is IC miss. + __ j(ZERO, ic_miss_label); + } + + // Part B: Load receiver's class, compare with all known classes. + __ movl(EBX, FieldAddress(EAX, Object::class_offset())); + for (int cli = 0; cli < classes.length(); cli++) { + const Class* test_class = classes[cli]; + ASSERT(!test_class->IsNullClass()); + if (test_class->raw() != smi_class.raw()) { + const Code& target = Code::Handle(targets[cli]->code()); + ExternalLabel target_label("ICtoTargetClass", target.EntryPoint()); + __ CompareObject(EBX, *test_class); + __ j(EQUAL, &target_label); + } + } + // IC miss. If null jump to megamorphic (don't trash IC), otherwise to IC + // miss in order to update the IC. + const Immediate raw_null = + Immediate(reinterpret_cast(Object::null())); + __ cmpl(EAX, raw_null); + __ j(EQUAL, &StubCode::MegamorphicLookupLabel()); + + __ jmp(ic_miss_label); + ic_stub_code = Code::FinalizeCode("inline cache stub", &assembler); + ICStubs::AppendICStubToTargets(targets, classes, ic_stub_code); + + if (FLAG_trace_icstub_generation) { + OS::Print("IC Stub code generated at 0x%x: targets: %d, classes: %d\n", + ic_stub_code.EntryPoint(), targets.length(), classes.length()); + for (intptr_t i = 0; i < targets.length(); i++) { + OS::Print(" target: 0x%x class: %s\n", + Code::Handle(targets[i]->code()).EntryPoint(), + classes[i]->ToCString()); + } + } + + if (FLAG_disassemble_stubs) { + for (intptr_t i = 0; i < classes.length(); i++) { + ASSERT(classes[i]->raw() != Object::null_class()); + const String& class_name = String::Handle(classes[i]->Name()); + CodeIndexTable* code_index_table = Isolate::Current()->code_index_table(); + const Code& target = Code::Handle(targets[i]->code()); + const Function& function = + Function::Handle( + code_index_table->LookupFunction(target.EntryPoint())); + OS::Print("%d: Code for inline cache for class '%s' function '%s': {\n", + i, class_name.ToCString(), function.ToFullyQualifiedCString()); + } + Disassembler::Disassemble(ic_stub_code.EntryPoint(), + ic_stub_code.EntryPoint() + assembler.CodeSize()); + OS::Print("}\n"); + } + + ICData ic_data(ic_stub_code); + ic_data.SetICDataArray(1, classes.length()); + ASSERT(classes.length() == targets.length()); + for (intptr_t i = 0; i < classes.length(); i++) { + GrowableArray temp_classes; + temp_classes.Add(classes[i]); + ic_data.SetCheckAt(i, temp_classes, *(targets[i])); + } + return ic_stub_code.raw(); +#undef __ +} + + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/ic_stubs_ia32_test.cc b/runtime/vm/ic_stubs_ia32_test.cc new file mode 100644 index 00000000000..90bd41b8af2 --- /dev/null +++ b/runtime/vm/ic_stubs_ia32_test.cc @@ -0,0 +1,276 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" +#if defined(TARGET_ARCH_IA32) + +#include "vm/assembler.h" +#include "vm/code_index_table.h" +#include "vm/ic_stubs.h" +#include "vm/stub_code.h" +#include "vm/unit_test.h" + +namespace dart { + + +#define __ assembler-> + +ASSEMBLER_TEST_GENERATE(NotAnIc, assembler) { + __ nop(); + __ addl(EAX, Immediate(1)); + __ ret(); +} + +#undef __ + +ASSEMBLER_TEST_RUN(NotAnIc, entry) { + GrowableArray classes; + GrowableArray targets; + bool is_ic = ICStubs::RecognizeICStub(entry, &classes, &targets); + EXPECT_EQ(false, is_ic); + EXPECT_EQ(0, classes.length()); + EXPECT_EQ(0, targets.length()); +} + + +TEST_CASE(UnresolvedIcTest) { + GrowableArray classes; + GrowableArray targets; + uword entry = StubCode::CallInstanceFunctionLabel().address(); + bool is_ic = ICStubs::RecognizeICStub(entry, &classes, &targets); + EXPECT_EQ(true, is_ic); + EXPECT_EQ(0, classes.length()); + EXPECT_EQ(0, targets.length()); +} + + +static RawFunction* GetDummyTarget(const char* name) { + Assembler assembler; + assembler.ret(); + const Code& code = + Code::Handle(Code::FinalizeCode(name, &assembler)); + const String& function_name = + String::ZoneHandle(String::NewSymbol(name)); + const Function& function = Function::Handle(Function::New( + function_name, RawFunction::kFunction, true, false, 0)); + function.SetCode(code); + CodeIndexTable* code_index_table = Isolate::Current()->code_index_table(); + ASSERT(code_index_table != NULL); + code_index_table->AddFunction(function); + return function.raw(); +} + + +TEST_CASE(SmiIcTest) { + const Function& function = Function::Handle(GetDummyTarget("Dummy")); + GrowableArray classes; + GrowableArray targets; + classes.Add( + &Class::ZoneHandle(Isolate::Current()->object_store()->smi_class())); + targets.Add(&function); + const Code& ic_stub = + Code::Handle(ICStubs::GetICStub(classes, targets)); + ASSERT(!ic_stub.IsNull()); + classes.Clear(); + targets.Clear(); + bool is_ic = ICStubs::RecognizeICStub( + ic_stub.EntryPoint(), &classes, &targets); + EXPECT_EQ(true, is_ic); + EXPECT(classes.length() == targets.length()); + EXPECT_EQ(1, classes.length()); + EXPECT(classes[0]->raw() == Isolate::Current()->object_store()->smi_class()); + EXPECT(targets[0]->raw() == function.raw()); +} + + +TEST_CASE(NonSmiIcTest) { + const Function& function = Function::Handle(GetDummyTarget("Dummy")); + GrowableArray classes; + GrowableArray targets; + classes.Add( + &Class::ZoneHandle(Isolate::Current()->object_store()->array_class())); + targets.Add(&function); + Code& ic_stub = Code::Handle(ICStubs::GetICStub(classes, targets)); + ASSERT(!ic_stub.IsNull()); + classes.Clear(); + targets.Clear(); + bool is_ic = + ICStubs::RecognizeICStub(ic_stub.EntryPoint(), &classes, &targets); + EXPECT_EQ(true, is_ic); + EXPECT(classes.length() == targets.length()); + EXPECT_EQ(1, classes.length()); + EXPECT(classes[0]->raw() == + Isolate::Current()->object_store()->array_class()); + EXPECT(targets[0]->raw() == function.raw()); + + // Also check for always-ic-miss case (e.g. with null receiver). + classes.Clear(); + targets.Clear(); + ic_stub = ICStubs::GetICStub(classes, targets); + EXPECT(classes.length() == targets.length()); + EXPECT(classes.is_empty()); +} + +TEST_CASE(MixedIcTest) { + const Function& function = Function::Handle(GetDummyTarget("Dummy")); + GrowableArray classes; + GrowableArray targets; + classes.Add( + &Class::ZoneHandle(Isolate::Current()->object_store()->array_class())); + targets.Add(&function); + classes.Add( + &Class::ZoneHandle(Isolate::Current()->object_store()->smi_class())); + targets.Add(&function); + const Code& ic_stub = Code::Handle(ICStubs::GetICStub(classes, targets)); + ASSERT(!ic_stub.IsNull()); + GrowableArray new_classes; + GrowableArray new_targets; + bool is_ic = ICStubs::RecognizeICStub( + ic_stub.EntryPoint(), &new_classes, &new_targets); + EXPECT_EQ(true, is_ic); + EXPECT(new_classes.length() == new_targets.length()); + EXPECT_EQ(2, new_classes.length()); + for (int i = 0; i < classes.length(); i++) { + EXPECT(ICStubs::IndexOfClass(new_classes, *classes[i]) >= 0); + EXPECT(targets[i]->raw() == function.raw()); + } +} + + +TEST_CASE(ManyClassesICTest) { + const Function& function1 = Function::Handle(GetDummyTarget("Dummy1")); + const Function& function2 = Function::Handle(GetDummyTarget("Dummy2")); + GrowableArray classes; + GrowableArray targets; + classes.Add( + &Class::ZoneHandle(Isolate::Current()->object_store()->array_class())); + targets.Add(&function1); + classes.Add( + &Class::ZoneHandle(Isolate::Current()->object_store()->double_class())); + targets.Add(&function1); + classes.Add( + &Class::ZoneHandle(Isolate::Current()->object_store()->bool_class())); + targets.Add(&function2); + EXPECT_EQ(3, classes.length()); + const Code& ic_stub = Code::Handle(ICStubs::GetICStub(classes, targets)); + ASSERT(!ic_stub.IsNull()); + GrowableArray new_classes; + GrowableArray new_targets; + bool is_ic = ICStubs::RecognizeICStub( + ic_stub.EntryPoint(), &new_classes, &new_targets); + EXPECT_EQ(true, is_ic); + EXPECT(new_classes.length() == new_targets.length()); + EXPECT_EQ(3, new_classes.length()); + for (int i = 0; i < new_classes.length(); i++) { + if (new_classes[i]->raw() == + Isolate::Current()->object_store()->array_class()) { + EXPECT_EQ(function1.raw(), new_targets[i]->raw()); + } else if (new_classes[i]->raw() == + Isolate::Current()->object_store()->double_class()) { + EXPECT_EQ(function1.raw(), new_targets[i]->raw()); + } else if (new_classes[i]->raw() == + Isolate::Current()->object_store()->bool_class()) { + EXPECT_EQ(function2.raw(), new_targets[i]->raw()); + } else { + UNREACHABLE(); + } + } + ICStubs::PatchTargets(ic_stub.EntryPoint(), + Code::Handle(function1.code()).EntryPoint(), + Code::Handle(function2.code()).EntryPoint()); + is_ic = ICStubs::RecognizeICStub( + ic_stub.EntryPoint(), &new_classes, &new_targets); + EXPECT_EQ(true, is_ic); + EXPECT(new_classes.length() == new_targets.length()); + EXPECT_EQ(3, new_classes.length()); + for (int i = 0; i < new_classes.length(); i++) { + if (new_classes[i]->raw() == + Isolate::Current()->object_store()->array_class()) { + EXPECT_EQ(function2.raw(), new_targets[i]->raw()); + } else if (new_classes[i]->raw() == + Isolate::Current()->object_store()->double_class()) { + EXPECT_EQ(function2.raw(), new_targets[i]->raw()); + } else if (new_classes[i]->raw() == + Isolate::Current()->object_store()->bool_class()) { + EXPECT_EQ(function2.raw(), new_targets[i]->raw()); + } else { + UNREACHABLE(); + } + } +} + + +static bool SameClassArrays(const GrowableArray& a, + const GrowableArray& b) { + if (a.length() != b.length()) { + return false; + } + for (int i = 0; i < a.length(); i++) { + if (a[i]->raw() != b[i]->raw()) { + return false; + } + } + return true; +} + + +// Testing the platform independent ICData class here because ICStubs::GetICStub +// is implemented only in ia32. +TEST_CASE(ICDataTest) { + GrowableArray classes; + GrowableArray targets; + const Code& ic_stub = Code::Handle(ICStubs::GetICStub(classes, targets)); + if (ic_stub.IsNull()) { + // ICStubs not implemented for the platform. + return; + } + ICData ic_data(ic_stub); + intptr_t num_classes = 2; + intptr_t num_checks = 3; + ic_data.SetICDataArray(num_classes, num_checks); + EXPECT_EQ(num_classes, ic_data.NumberOfClasses()); + EXPECT_EQ(num_checks, ic_data.NumberOfChecks()); + ObjectStore* object_store = Isolate::Current()->object_store(); + const Class& double_class = Class::ZoneHandle(object_store->double_class()); + const Class& smi_class = Class::ZoneHandle(object_store->smi_class()); + GrowableArray check0; + check0.Add(&smi_class); + check0.Add(&smi_class); + GrowableArray check1; + check1.Add(&smi_class); + check1.Add(&double_class); + GrowableArray check2; + check2.Add(&double_class); + check2.Add(&smi_class); + const Function& function0 = Function::ZoneHandle(GetDummyTarget("SmiSmi")); + const Function& function1 = Function::ZoneHandle(GetDummyTarget("SmiDouble")); + const Function& function2 = Function::ZoneHandle(GetDummyTarget("DoubleSmi")); + ic_data.SetCheckAt(0, check0, function0); + ic_data.SetCheckAt(1, check1, function1); + ic_data.SetCheckAt(2, check2, function2); + + Function& test_function = Function::Handle(); + GrowableArray test_classes; + + ic_data.GetCheckAt(0, &test_classes, &test_function); + EXPECT_EQ(function0.raw(), test_function.raw()); + EXPECT_EQ(true, SameClassArrays(test_classes, check0)); + + ic_data.GetCheckAt(1, &test_classes, &test_function); + EXPECT_EQ(function1.raw(), test_function.raw()); + EXPECT_EQ(true, SameClassArrays(test_classes, check1)); + + ic_data.GetCheckAt(1, &test_classes, &test_function); + EXPECT_EQ(function1.raw(), test_function.raw()); + EXPECT_EQ(true, SameClassArrays(test_classes, check1)); + + ic_data.GetCheckAt(2, &test_classes, &test_function); + EXPECT_EQ(function2.raw(), test_function.raw()); + EXPECT_EQ(true, SameClassArrays(test_classes, check2)); +} + + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/ic_stubs_x64.cc b/runtime/vm/ic_stubs_x64.cc new file mode 100644 index 00000000000..b5305cc7690 --- /dev/null +++ b/runtime/vm/ic_stubs_x64.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_X64. +#if defined(TARGET_ARCH_X64) + +#include "vm/ic_stubs.h" + +#include "vm/object.h" + +namespace dart { + +RawCode* ICStubs::GetICStub(const GrowableArray& classes, + const GrowableArray& targets) { + UNIMPLEMENTED(); + return Code::null(); +} + + +bool ICStubs::RecognizeICStub(uword entry_point, + GrowableArray* classes, + GrowableArray* targets) { + UNIMPLEMENTED(); + return false; +} + + +int ICStubs::IndexOfClass(const GrowableArray& classes, + const Class& cls) { + UNIMPLEMENTED(); + return false; +} + + +void ICStubs::PatchTargets(uword ic_entry_point, uword from, uword to) { + UNIMPLEMENTED(); +} + +} // namespace dart + +#endif // defined TARGET_ARCH_X64 diff --git a/runtime/vm/instructions.h b/runtime/vm/instructions.h new file mode 100644 index 00000000000..3b32e63456f --- /dev/null +++ b/runtime/vm/instructions.h @@ -0,0 +1,20 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_INSTRUCTIONS_H_ +#define VM_INSTRUCTIONS_H_ + +#include "vm/globals.h" + +#if defined(TARGET_ARCH_IA32) +#include "vm/instructions_ia32.h" +#elif defined(TARGET_ARCH_X64) +// No instruction patterns implemented. +#elif defined(TARGET_ARCH_ARM) +// No instruction patterns implemented. +#else +#error Unknown architecture. +#endif + +#endif // VM_INSTRUCTIONS_H_ diff --git a/runtime/vm/instructions_ia32.cc b/runtime/vm/instructions_ia32.cc new file mode 100644 index 00000000000..24b8b8bf197 --- /dev/null +++ b/runtime/vm/instructions_ia32.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. +#if defined(TARGET_ARCH_IA32) + +#include "vm/instructions.h" +#include "vm/object.h" + +namespace dart { + +bool Instruction::TestBytesWith(const int* data, int num_bytes) const { + ASSERT(data != NULL); + const uint8_t* byte_array = reinterpret_cast(start_); + for (int i = 0; i < num_bytes; i++) { + // Skip comparison for data[i] < 0. + if ((data[i] >= 0) && (byte_array[i] != (0xFF & data[i]))) { + return false; + } + } + return true; +} + + +const int* ICLoadReceiver::pattern() const { + static const int kLoadReceiverPattern[kLengthInBytes] = + {0x8b, 0x42, 0x0b, 0x8b, 0x04, 0x44}; + return kLoadReceiverPattern; +} + + +uword JumpIfZero::TargetAddress() const { + ASSERT(IsValid()); + return start() + kLengthInBytes + *reinterpret_cast(start() + 2); +} + + +void JumpIfZero::SetTargetAddress(uword pc) { + ASSERT(IsValid()); + *reinterpret_cast(start() + 2) = pc - start() - kLengthInBytes; +} + + +const int* JumpIfZero::pattern() const { + static const int kJzPattern[kLengthInBytes] = {0x0f, 0x84, -1, -1, -1, -1}; + return kJzPattern; +} + + +const int* CmpEaxWithImmediate::pattern() const { + static const int kCmpWithImmediate[kLengthInBytes] = {0x3d, -1, -1, -1, -1}; + return kCmpWithImmediate; +} + + +const int* TestEaxIsSmi::pattern() const { + static const int + kTestSmiTag[kTestLengthInBytes + JumpIfZero::kLengthInBytes] = + { 0xa8, 0x01, -1, -1, -1, -1, -1, -1}; + return kTestSmiTag; +} + + +const int* ICCheckReceiverClass::pattern() const { + static const int kTestClass[kTestLengthInBytes + JumpIfZero::kLengthInBytes] = + {0x81, 0xfb, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + return kTestClass; +} + + +RawClass* ICCheckReceiverClass::TestClass() const { + ASSERT(IsValid()); + Class& cls = Class::Handle(); + cls ^= *reinterpret_cast(start() + 2); + return cls.raw(); +} + + +const int* LoadObjectClass::pattern() const { + static const int kTestClass[kLoadObjectClassLengthInBytes] = + {0x8b, 0x58, 0xff}; + return kTestClass; +} + +uword CallOrJump::TargetAddress() const { + ASSERT(IsValid()); + return start() + kLengthInBytes + *reinterpret_cast(start() + 1); +} + + +void CallOrJump::SetTargetAddress(uword target) const { + ASSERT(IsValid()); + *reinterpret_cast(start() + 1) = target - start() - kLengthInBytes; +} + + +const int* Call::pattern() const { + static const int kCallPattern[kLengthInBytes] = {0xE8, -1, -1, -1, -1}; + return kCallPattern; +} + + +const int* Jump::pattern() const { + static const int kJumpPattern[kLengthInBytes] = {0xE9, -1, -1, -1, -1}; + return kJumpPattern; +} + + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/instructions_ia32.h b/runtime/vm/instructions_ia32.h new file mode 100644 index 00000000000..df4a33ce79d --- /dev/null +++ b/runtime/vm/instructions_ia32.h @@ -0,0 +1,252 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Classes that describe assembly patterns as used by inline caches. + +#ifndef VM_INSTRUCTIONS_IA32_H_ +#define VM_INSTRUCTIONS_IA32_H_ + +#ifndef VM_INSTRUCTIONS_H_ +#error Do not include instructions_ia32.h directly; use instructions.h instead. +#endif + +#include "vm/allocation.h" + +namespace dart { + +// Forward declarations. +class RawClass; +class Immediate; +class RawObject; + +// Abstract class for all instruction pattern classes. +class Instruction : public ValueObject { + public: + explicit Instruction(uword pc) : start_(pc) { + ASSERT(pc != 0); + } + virtual ~Instruction() {} + + // Call to check if the instruction pattern at 'pc' match the instruction. + virtual bool IsValid() const { + return TestBytesWith(pattern(), pattern_length_in_bytes()); + } + + // 'pattern' returns the expected byte pattern in form of an integer array + // with length of 'pattern_length_in_bytes'. A '-1' element means 'any byte'. + virtual const int* pattern() const = 0; + virtual int pattern_length_in_bytes() const = 0; + + protected: + uword start() const { return start_; } + + private: + // Returns true if the 'num_bytes' bytes at 'start_' correspond to + // array of integers 'data'. 'data' elements are either a byte or -1, which + // represents any byte. + bool TestBytesWith(const int* data, int num_bytes) const; + + const uword start_; + + DISALLOW_COPY_AND_ASSIGN(Instruction); +}; + + +// Pattern to load receiver from stack into EAX, with caller's return +// address on TOS and EDX containing the number of arguments. Pattern: +// 'mov eax, 0xb(edx)'. +// 'mov eax, (esp+eax*0x2)'. +class ICLoadReceiver : public Instruction { + public: + explicit ICLoadReceiver(uword pc) : Instruction(pc) {} + + virtual int pattern_length_in_bytes() const { + return kLengthInBytes; + } + + private: + virtual const int* pattern() const; + + static const int kLengthInBytes = 6; + + DISALLOW_COPY_AND_ASSIGN(ICLoadReceiver); +}; + + +// Pattern for a conditional jump (if zero) to a far address. Pattern: +// 'jz ' +class JumpIfZero : public Instruction { + public: + explicit JumpIfZero(uword pc) : Instruction(pc) {} + uword TargetAddress() const; + virtual int pattern_length_in_bytes() const { + return kLengthInBytes; + } + + void SetTargetAddress(uword pc); + + private: + friend class TestEaxIsSmi; + friend class ICCheckReceiverClass; + + virtual const int* pattern() const; + + static const int kLengthInBytes = 6; + + DISALLOW_COPY_AND_ASSIGN(JumpIfZero); +}; + + +// Pattern for comparison of an immediate with EAX. Pattern: +// 'cmp eax, '. +class CmpEaxWithImmediate : public Instruction { + public: + explicit CmpEaxWithImmediate(uword pc) : Instruction(pc) {} + Immediate* immediate() const { + ASSERT(IsValid()); + return reinterpret_cast(start() + 1); + } + virtual int pattern_length_in_bytes() const { + return kLengthInBytes; + } + + private: + virtual const int* pattern() const; + + static const int kLengthInBytes = 5; + + DISALLOW_COPY_AND_ASSIGN(CmpEaxWithImmediate); +}; + + +// Pattern to test if EAX contains a Smi. Pattern: +// 'test al, 0x1' +// 'jz '. +class TestEaxIsSmi : public Instruction { + public: + explicit TestEaxIsSmi(uword pc) + : Instruction(pc), + jz_(pc + kTestLengthInBytes) {} + uword TargetAddress() const { + ASSERT(IsValid()); + return jz_.TargetAddress(); + } + + void SetTargetAddress(uword new_target) { + ASSERT(IsValid()); + jz_.SetTargetAddress(new_target); + ASSERT(IsValid()); + } + + virtual int pattern_length_in_bytes() const { + return kTestLengthInBytes + jz_.pattern_length_in_bytes(); + } + virtual bool IsValid() const { + return Instruction::IsValid() && jz_.IsValid(); + } + + private: + virtual const int* pattern() const; + + static const int kTestLengthInBytes = 2; + JumpIfZero jz_; + + DISALLOW_COPY_AND_ASSIGN(TestEaxIsSmi); +}; + + +// Pattern for checking class of the receiver (class in EBX). Pattern: +// cmp ebx, +// jz ' +class ICCheckReceiverClass : public Instruction { + public: + explicit ICCheckReceiverClass(uword pc) + : Instruction(pc), + jz_(pc + kTestLengthInBytes) {} + virtual int pattern_length_in_bytes() const { + return kTestLengthInBytes + jz_.pattern_length_in_bytes(); + } + virtual bool IsValid() const { + return Instruction::IsValid() && jz_.IsValid(); + } + uword TargetAddress() const { + ASSERT(IsValid()); + return jz_.TargetAddress(); + } + RawClass* TestClass() const; + + void SetTargetAddress(uword new_target) { + ASSERT(IsValid()); + jz_.SetTargetAddress(new_target); + ASSERT(IsValid()); + } + + private: + virtual const int* pattern() const; + + static const int kTestLengthInBytes = 6; + JumpIfZero jz_; + + DISALLOW_COPY_AND_ASSIGN(ICCheckReceiverClass); +}; + + +class LoadObjectClass : public Instruction { + public: + explicit LoadObjectClass(uword pc) : Instruction(pc) {} + virtual int pattern_length_in_bytes() const { + return kLoadObjectClassLengthInBytes; + } + private: + virtual const int* pattern() const; + static const int kLoadObjectClassLengthInBytes = 3; + + DISALLOW_COPY_AND_ASSIGN(LoadObjectClass); +}; + + +class CallOrJump : public Instruction { + public: + virtual int pattern_length_in_bytes() const { + return kLengthInBytes; + } + uword TargetAddress() const; + void SetTargetAddress(uword new_target) const; + + protected: + explicit CallOrJump(uword pc) : Instruction(pc) {} + static const int kLengthInBytes = 5; + + private: + DISALLOW_COPY_AND_ASSIGN(CallOrJump); +}; + + +class Call : public CallOrJump { + public: + explicit Call(uword pc) : CallOrJump(pc) {} + static int InstructionLength() { + return kLengthInBytes; + } + + private: + virtual const int* pattern() const; + + DISALLOW_COPY_AND_ASSIGN(Call); +}; + + +class Jump : public CallOrJump { + public: + explicit Jump(uword pc) : CallOrJump(pc) {} + + private: + virtual const int* pattern() const; + + DISALLOW_COPY_AND_ASSIGN(Jump); +}; + + +} // namespace dart + +#endif // VM_INSTRUCTIONS_IA32_H_ diff --git a/runtime/vm/instructions_ia32_test.cc b/runtime/vm/instructions_ia32_test.cc new file mode 100644 index 00000000000..69483397ff3 --- /dev/null +++ b/runtime/vm/instructions_ia32_test.cc @@ -0,0 +1,156 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" +#if defined(TARGET_ARCH_IA32) + +#include "vm/assembler.h" +#include "vm/instructions.h" +#include "vm/stub_code.h" +#include "vm/unit_test.h" + +namespace dart { + +#define __ assembler-> + +ASSEMBLER_TEST_GENERATE(NotAny, assembler) { + __ nop(); + __ ret(); +} + +ASSEMBLER_TEST_RUN(NotAny, entry) { + ICLoadReceiver load(entry); + EXPECT(!load.IsValid()); + JumpIfZero jump(entry); + EXPECT(!jump.IsValid()); + CmpEaxWithImmediate cmp(entry); + EXPECT(!cmp.IsValid()); + TestEaxIsSmi test(entry); + EXPECT(!test.IsValid()); + ICCheckReceiverClass check_class(entry); + EXPECT(!check_class.IsValid()); +} + + +ASSEMBLER_TEST_GENERATE(ICLoadReceiver, assembler) { + __ movl(EAX, FieldAddress(EDX, Array::data_offset())); + __ movl(EAX, Address(ESP, EAX, TIMES_2, 0)); + __ ret(); +} + +ASSEMBLER_TEST_RUN(ICLoadReceiver, entry) { + ICLoadReceiver load(entry); + EXPECT(load.IsValid()); +} + + +ASSEMBLER_TEST_GENERATE(JumpIfZero, assembler) { + __ j(ZERO, &StubCode::MegamorphicLookupLabel()); + __ ret(); +} + +ASSEMBLER_TEST_RUN(JumpIfZero, entry) { + JumpIfZero jump(entry); + EXPECT(jump.IsValid()); + EXPECT_EQ(StubCode::MegamorphicLookupLabel().address(), jump.TargetAddress()); +} + + +ASSEMBLER_TEST_GENERATE(CmpEaxWithImmediate, assembler) { + const Immediate raw_null = + Immediate(reinterpret_cast(Object::null())); + __ cmpl(EAX, raw_null); + __ ret(); +} + +ASSEMBLER_TEST_RUN(CmpEaxWithImmediate, entry) { + CmpEaxWithImmediate cmp(entry); + EXPECT(cmp.IsValid()); + const Immediate raw_null = + Immediate(reinterpret_cast(Object::null())); + EXPECT_EQ(raw_null.value(), cmp.immediate()->value()); +} + + +ASSEMBLER_TEST_GENERATE(TestEaxIsSmi, assembler) { + __ testl(EAX, Immediate(kSmiTagMask)); + __ j(ZERO, &StubCode::MegamorphicLookupLabel()); + __ ret(); +} + +ASSEMBLER_TEST_RUN(TestEaxIsSmi, entry) { + TestEaxIsSmi test(entry); + EXPECT(test.IsValid()); + EXPECT_EQ(StubCode::MegamorphicLookupLabel().address(), test.TargetAddress()); +} + + +ASSEMBLER_TEST_GENERATE(ICCheckReceiverClass, assembler) { + const Class& test_class = + Class::ZoneHandle(Isolate::Current()->object_store()->double_class()); + __ CompareObject(EBX, test_class); + __ j(ZERO, &StubCode::MegamorphicLookupLabel()); + __ ret(); +} + +ASSEMBLER_TEST_RUN(ICCheckReceiverClass, entry) { + ICCheckReceiverClass class_check(entry); + EXPECT(class_check.IsValid()); + EXPECT_EQ(Isolate::Current()->object_store()->double_class(), + class_check.TestClass()); + EXPECT_EQ(StubCode::MegamorphicLookupLabel().address(), + class_check.TargetAddress()); +} + + +ASSEMBLER_TEST_GENERATE(LoadObjectClass, assembler) { + __ movl(EBX, FieldAddress(EAX, Object::class_offset())); + __ ret(); +} + +ASSEMBLER_TEST_RUN(LoadObjectClass, entry) { + LoadObjectClass load(entry); + EXPECT(load.IsValid()); +} + + +ASSEMBLER_TEST_GENERATE(Call, assembler) { + __ call(&StubCode::MegamorphicLookupLabel()); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(Call, entry) { + Call call(entry); + EXPECT_EQ(StubCode::MegamorphicLookupLabel().address(), call.TargetAddress()); +} + + +ASSEMBLER_TEST_GENERATE(Jump, assembler) { + __ jmp(&StubCode::MegamorphicLookupLabel()); + __ jmp(&StubCode::CallInstanceFunctionLabel()); + __ ret(); +} + + +ASSEMBLER_TEST_RUN(Jump, entry) { + Jump jump1(entry); + EXPECT_EQ(StubCode::MegamorphicLookupLabel().address(), + jump1.TargetAddress()); + Jump jump2(entry + jump1.pattern_length_in_bytes()); + EXPECT_EQ(StubCode::CallInstanceFunctionLabel().address(), + jump2.TargetAddress()); + uword target1 = jump1.TargetAddress(); + uword target2 = jump2.TargetAddress(); + jump1.SetTargetAddress(target2); + jump2.SetTargetAddress(target1); + EXPECT_EQ(StubCode::CallInstanceFunctionLabel().address(), + jump1.TargetAddress()); + EXPECT_EQ(StubCode::MegamorphicLookupLabel().address(), + jump2.TargetAddress()); +} + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/intrinsifier.h b/runtime/vm/intrinsifier.h new file mode 100644 index 00000000000..ad78703f77b --- /dev/null +++ b/runtime/vm/intrinsifier.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Class for intrinsifying functions. + +#ifndef VM_INTRINSIFIER_H_ +#define VM_INTRINSIFIER_H_ + +#include "vm/allocation.h" + +namespace dart { + +// Forward declarations. +class Assembler; +class Function; + +class Intrinsifier : public AllStatic { + public: + // Try to intrinsify 'function'. Returns true if the function intrinsified + // completely and the code does not need to be generated (i.e., no slow + // path possible). + static bool Intrinsify(const Function& function, Assembler* assembler); +}; + +} // namespace dart + +#endif // VM_INTRINSIFIER_H_ diff --git a/runtime/vm/intrinsifier_arm.cc b/runtime/vm/intrinsifier_arm.cc new file mode 100644 index 00000000000..d406b0d4f31 --- /dev/null +++ b/runtime/vm/intrinsifier_arm.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM. +#if defined(TARGET_ARCH_ARM) + +#include "vm/intrinsifier.h" + +namespace dart { + +bool Intrinsifier::Intrinsify(const Function& function, Assembler* assembler) { + return false; +} + +} // namespace dart + +#endif // defined TARGET_ARCH_ARM diff --git a/runtime/vm/intrinsifier_ia32.cc b/runtime/vm/intrinsifier_ia32.cc new file mode 100644 index 00000000000..d18a8c8276c --- /dev/null +++ b/runtime/vm/intrinsifier_ia32.cc @@ -0,0 +1,880 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// The intrinsic code below is executed before a method has built its frame. +// The return address is on the stack and the arguments below it. +// Registers EDX (arguments descriptor) and ECX (function) must be preserved. +// Each intrinsification method returns true if the corresponding +// Dart method was intrinsified. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. +#if defined(TARGET_ARCH_IA32) + +#include "vm/intrinsifier.h" + +#include "vm/assembler.h" +#include "vm/assembler_macros.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/os.h" +#include "vm/stub_code.h" + +namespace dart { + +DEFINE_FLAG(bool, intrinsify, true, "Instrinsify when possible"); + +// List of intrinsics: (class-name, function-name, intrinsification method). +#define INTRINSIC_LIST(V) \ + V(IntegerImplementation, addFromInteger, Integer_addFromInteger) \ + V(IntegerImplementation, +, Integer_addFromInteger) \ + V(IntegerImplementation, subFromInteger, Integer_subFromInteger) \ + V(IntegerImplementation, -, Integer_sub) \ + V(IntegerImplementation, mulFromInteger, Integer_mulFromInteger) \ + V(IntegerImplementation, *, Integer_mulFromInteger) \ + V(IntegerImplementation, %, Integer_modulo) \ + V(IntegerImplementation, negate, Integer_negate) \ + V(IntegerImplementation, bitAndFromInteger, Integer_bitAndFromInteger) \ + V(IntegerImplementation, &, Integer_bitAndFromInteger) \ + V(IntegerImplementation, bitOrFromInteger, Integer_bitOrFromInteger) \ + V(IntegerImplementation, |, Integer_bitOrFromInteger) \ + V(IntegerImplementation, bitXorFromInteger, Integer_bitXorFromInteger) \ + V(IntegerImplementation, ^, Integer_bitXorFromInteger) \ + V(IntegerImplementation, greaterThanFromInteger, Integer_lessThan) \ + V(IntegerImplementation, >, Integer_greaterThan) \ + V(IntegerImplementation, ==, Integer_equalToInteger) \ + V(IntegerImplementation, equalToInteger, Integer_equalToInteger) \ + V(IntegerImplementation, <, Integer_lessThan) \ + V(IntegerImplementation, <=, Integer_lessEqualThan) \ + V(IntegerImplementation, >=, Integer_greaterEqualThan) \ + V(IntegerImplementation, <<, Integer_shl) \ + V(IntegerImplementation, >>, Integer_sar) \ + V(Smi, ~, Smi_bitNegate) \ + V(Double, >, Double_greaterThan) \ + V(Double, >=, Double_greaterEqualThan) \ + V(Double, <, Double_lessThan) \ + V(Double, <=, Double_lessEqualThan) \ + V(Double, ==, Double_equal) \ + V(Double, +, Double_add) \ + V(Double, -, Double_sub) \ + V(Double, *, Double_mul) \ + V(Double, /, Double_div) \ + V(Double, toDouble, Double_toDouble) \ + V(Double, mulFromInteger, Double_mulFromInteger) \ + V(ObjectArray, ObjectArray., ObjectArray_Allocate) \ + V(ObjectArray, get:length, Array_getLength) \ + V(ObjectArray, [], Array_getIndexed) \ + V(ObjectArray, []=, Array_setIndexed) \ + V(GrowableObjectArray, get:length, GrowableArray_getLength) \ + V(GrowableObjectArray, [], GrowableArray_getIndexed) \ + V(ImmutableArray, [], Array_getIndexed) \ + V(ImmutableArray, get:length, Array_getLength) \ + V(Math, sqrt, Math_sqrt) \ + V(Object, ==, Object_equal) \ + V(FixedSizeArrayIterator, next, FixedSizeArrayIterator_next) \ + V(FixedSizeArrayIterator, hasNext, FixedSizeArrayIterator_hasNext) \ + +#define __ assembler-> + +static bool ObjectArray_Allocate(Assembler* assembler) { + // This snippet of inlined code uses the following registers: + // EAX, EBX, EDI + // and the newly allocated object is returned in EAX. + const intptr_t kTypeArgumentsOffset = 2 * kWordSize; + const intptr_t kArrayLengthOffset = 1 * kWordSize; + Label fall_through; + + // Compute the size to be allocated, it is based on the array length + // and it computed as: + // RoundedAllocationSize((array_length * kwordSize) + sizeof(RawArray)). + __ movl(EDI, Address(ESP, kArrayLengthOffset)); // Array Length. + // Assert that length is a Smi. + __ testl(EDI, Immediate(kSmiTagSize)); + __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); + intptr_t fixed_size = sizeof(RawArray) + kObjectAlignment - 1; + __ leal(EDI, Address(EDI, TIMES_2, fixed_size)); // EDI is a Smi. + ASSERT(kSmiTagShift == 1); + __ andl(EDI, Immediate(-kObjectAlignment)); + + Heap* heap = Isolate::Current()->heap(); + + // EDI: size to allocate. + __ movl(EAX, Address::Absolute(heap->TopAddress())); + __ leal(EBX, Address(EAX, EDI, TIMES_1, 0)); + + // Check if the allocation fits into the remaining space. + // EAX: potential new object start. + // EBX: potential next object start. + __ cmpl(EBX, Address::Absolute(heap->EndAddress())); + __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump); + + // Successfully allocated the object(s), now update top to point to + // next object start and initialize the object. + __ movl(Address::Absolute(heap->TopAddress()), EBX); + __ addl(EAX, Immediate(kHeapObjectTag)); + + // EAX: new object start as a tagged pointer. + // EBX: new object end address. + // Store class value for array. + __ movl(EDI, FieldAddress(CTX, Context::isolate_offset())); + __ movl(EDI, Address(EDI, Isolate::object_store_offset())); + __ movl(EDI, Address(EDI, ObjectStore::array_class_offset())); + __ movl(FieldAddress(EAX, Instance::class_offset()), EDI); + + // Store the type argument field. + __ movl(EDI, Address(ESP, kTypeArgumentsOffset)); // type argument. + __ movl(FieldAddress(EAX, Array::type_arguments_offset()), EDI); + + // Set the length field. + __ movl(EDI, Address(ESP, kArrayLengthOffset)); // Array Length. + __ movl(FieldAddress(EAX, Array::length_offset()), EDI); + + // Initialize all array elements to raw_null. + // EAX: new object start as a tagged pointer. + // EBX: new object end address. + // EDI: iterator which initially points to the start of the variable + // data area to be initialized. + const Immediate raw_null = + Immediate(reinterpret_cast(Object::null())); + __ leal(EDI, FieldAddress(EAX, sizeof(RawArray))); + Label done; + Label init_loop; + __ Bind(&init_loop); + __ cmpl(EDI, EBX); + __ j(ABOVE_EQUAL, &done, Assembler::kNearJump); + __ movl(Address(EDI, 0), raw_null); + __ addl(EDI, Immediate(kWordSize)); + __ jmp(&init_loop, Assembler::kNearJump); + __ Bind(&done); + __ ret(); // returns the newly allocated object in EAX. + + __ Bind(&fall_through); + return false; +} + + +static bool Array_getLength(Assembler* assembler) { + __ movl(EAX, Address(ESP, + 1 * kWordSize)); + __ movl(EAX, FieldAddress(EAX, Array::length_offset())); + __ ret(); + return true; +} + + +static bool Array_getIndexed(Assembler* assembler) { + Label fall_through; + __ movl(EBX, Address(ESP, + 1 * kWordSize)); // Index. + __ movl(EAX, Address(ESP, + 2 * kWordSize)); // Array. + __ testl(EBX, Immediate(kSmiTagMask)); + __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); // Non-smi index. + // Range check. + __ cmpl(EBX, FieldAddress(EAX, Array::length_offset())); + // Runtime throws exception. + __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump); + // Note that EBX is Smi, i.e, times 2. + ASSERT(kSmiTagShift == 1); + __ movl(EAX, FieldAddress(EAX, EBX, TIMES_2, sizeof(RawArray))); + __ ret(); + __ Bind(&fall_through); + return false; +} + + +// Intrinsify only for Smi value and index. Non-smi values need a store buffer +// update. Array length is always a Smi. +static bool Array_setIndexed(Assembler* assembler) { + const Immediate raw_null = + Immediate(reinterpret_cast(Object::null())); + Label fall_through; + __ movl(EAX, Address(ESP, + 1 * kWordSize)); // Value. + __ movl(EBX, Address(ESP, + 2 * kWordSize)); // Index. + __ orl(EAX, EBX); + __ testl(EBX, Immediate(kSmiTagMask)); + // Value or index not Smi. + __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); + __ movl(EAX, Address(ESP, + 3 * kWordSize)); // Array. + // Range check. + __ cmpl(EBX, FieldAddress(EAX, Array::length_offset())); + // Runtime throws exception. + __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump); + // Note that EBX is Smi, i.e, times 2. + ASSERT(kSmiTagShift == 1); + // Destroy ECX as we will not continue in the function. + __ movl(ECX, Address(ESP, + 1 * kWordSize)); + __ movl(FieldAddress(EAX, EBX, TIMES_2, sizeof(RawArray)), ECX); + // Caller is responsible of preserving the value if necessary. + __ ret(); + __ Bind(&fall_through); + return false; +} + + +static intptr_t GetOffsetForField(const char* class_name_p, + const char* field_name_p) { + const String& class_name = String::Handle(String::NewSymbol(class_name_p)); + const String& field_name = String::Handle(String::NewSymbol(field_name_p)); + const Class& cls = Class::Handle(Library::Handle( + Library::CoreImplLibrary()).LookupClass(class_name)); + ASSERT(!cls.IsNull()); + const Field& field = Field::ZoneHandle(cls.LookupInstanceField(field_name)); + ASSERT(!field.IsNull()); + return field.Offset(); +} + + +static const char* kGrowableArrayClassName = "GrowableObjectArray"; +static const char* kGrowableArrayLengthFieldName = "_length"; +static const char* kGrowableArrayArrayFieldName = "backingArray"; + +// Read the length_ instance field. +static bool GrowableArray_getLength(Assembler* assembler) { + intptr_t length_offset = GetOffsetForField(kGrowableArrayClassName, + kGrowableArrayLengthFieldName); + __ movl(EAX, Address(ESP, + 1 * kWordSize)); + __ movl(EAX, FieldAddress(EAX, length_offset)); + __ ret(); + return true; +} + + +static bool GrowableArray_getIndexed(Assembler* assembler) { + intptr_t length_offset = GetOffsetForField(kGrowableArrayClassName, + kGrowableArrayLengthFieldName); + intptr_t array_offset = GetOffsetForField(kGrowableArrayClassName, + kGrowableArrayArrayFieldName); + Label fall_through; + __ movl(EBX, Address(ESP, + 1 * kWordSize)); // Index. + __ movl(EAX, Address(ESP, + 2 * kWordSize)); // GrowableArray. + __ testl(EBX, Immediate(kSmiTagMask)); + __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); // Non-smi index. + // Range check. + __ cmpl(EBX, FieldAddress(EAX, length_offset)); + // Runtime throws exception. + __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump); + __ movl(EAX, FieldAddress(EAX, array_offset)); // backingArray. + + // Note that EBX is Smi, i.e, times 2. + ASSERT(kSmiTagShift == 1); + __ movl(EAX, FieldAddress(EAX, EBX, TIMES_2, sizeof(RawArray))); + __ ret(); + __ Bind(&fall_through); + return false; +} + + +// Tests if two top most arguments are smis, jumps to label not_smi if not. +// Topmost argument is in EAX. +static void TestBothArgumentsSmis(Assembler* assembler, Label* not_smi) { + __ movl(EAX, Address(ESP, + 1 * kWordSize)); + __ movl(EBX, Address(ESP, + 2 * kWordSize)); + __ orl(EBX, EAX); + __ testl(EBX, Immediate(kSmiTagMask)); + __ j(NOT_ZERO, not_smi, Assembler::kNearJump); +} + + +static bool Integer_addFromInteger(Assembler* assembler) { + Label fall_through; + TestBothArgumentsSmis(assembler, &fall_through); + __ addl(EAX, Address(ESP, + 2 * kWordSize)); + __ j(OVERFLOW, &fall_through, Assembler::kNearJump); + // Result is in EAX. + __ ret(); + __ Bind(&fall_through); + return false; +} + + +static bool Integer_subFromInteger(Assembler* assembler) { + Label fall_through; + TestBothArgumentsSmis(assembler, &fall_through); + __ subl(EAX, Address(ESP, + 2 * kWordSize)); + __ j(OVERFLOW, &fall_through, Assembler::kNearJump); + // Result is in EAX. + __ ret(); + __ Bind(&fall_through); + return false; +} + + +static bool Integer_sub(Assembler* assembler) { + Label fall_through; + TestBothArgumentsSmis(assembler, &fall_through); + __ movl(EBX, EAX); + __ movl(EAX, Address(ESP, + 2 * kWordSize)); + __ subl(EAX, EBX); + __ j(OVERFLOW, &fall_through, Assembler::kNearJump); + // Result is in EAX. + __ ret(); + __ Bind(&fall_through); + return false; +} + + + +static bool Integer_mulFromInteger(Assembler* assembler) { + Label fall_through; + TestBothArgumentsSmis(assembler, &fall_through); + ASSERT(kSmiTag == 0); // Adjust code below if not the case. + __ SmiUntag(EAX); + __ imull(EAX, Address(ESP, + 2 * kWordSize)); + __ j(OVERFLOW, &fall_through, Assembler::kNearJump); + // Result is in EAX. + __ ret(); + __ Bind(&fall_through); + return false; +} + + +// Simple implementation: for positive dividend values greater than divisor, +// return dividend. +static bool Integer_modulo(Assembler* assembler) { + Label fall_through, return_zero; + TestBothArgumentsSmis(assembler, &fall_through); + // EAX: right argument (divisor) + __ movl(EBX, Address(ESP, + 2 * kWordSize)); // Left argument (dividend). + __ cmpl(EBX, Immediate(0)); + __ j(LESS, &fall_through, Assembler::kNearJump); + __ cmpl(EBX, EAX); + __ j(EQUAL, &return_zero, Assembler::kNearJump); + __ j(GREATER, &fall_through, Assembler::kNearJump); + __ movl(EAX, EBX); // Return dividend. + __ ret(); + __ Bind(&return_zero); + __ xorl(EAX, EAX); // Return zero. + __ ret(); + __ Bind(&fall_through); + return false; +} + + +static bool Integer_negate(Assembler* assembler) { + Label fall_through; + __ movl(EAX, Address(ESP, + 1 * kWordSize)); + __ testl(EAX, Immediate(kSmiTagMask)); + __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); // Non-smi value. + __ negl(EAX); + __ j(OVERFLOW, &fall_through, Assembler::kNearJump); + // Result is in EAX. + __ ret(); + __ Bind(&fall_through); + return false; +} + + +static bool Integer_bitAndFromInteger(Assembler* assembler) { + Label fall_through; + TestBothArgumentsSmis(assembler, &fall_through); + __ movl(EBX, Address(ESP, + 2 * kWordSize)); + __ andl(EAX, EBX); + // Result is in EAX. + __ ret(); + __ Bind(&fall_through); + return false; +} + + +static bool Integer_bitOrFromInteger(Assembler* assembler) { + Label fall_through; + TestBothArgumentsSmis(assembler, &fall_through); + __ movl(EBX, Address(ESP, + 2 * kWordSize)); + __ orl(EAX, EBX); + // Result is in EAX. + __ ret(); + __ Bind(&fall_through); + return false; +} + + +static bool Integer_bitXorFromInteger(Assembler* assembler) { + Label fall_through; + TestBothArgumentsSmis(assembler, &fall_through); + __ movl(EBX, Address(ESP, + 2 * kWordSize)); + __ xorl(EAX, EBX); + // Result is in EAX. + __ ret(); + __ Bind(&fall_through); + return false; +} + + +static bool Integer_shl(Assembler* assembler) { + ASSERT(kSmiTagShift == 1); + ASSERT(kSmiTag == 0); + Label fall_through, overflow; + TestBothArgumentsSmis(assembler, &fall_through); + // Shift value is in EAX. Compare with tagged Smi. + __ cmpl(EAX, Immediate(Smi::RawValue(Smi::kBits))); + __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump); + + __ SmiUntag(EAX); + __ movl(ECX, EAX); // Shift amount must be in ECX. + __ movl(EAX, Address(ESP, + 2 * kWordSize)); // Value. + + // Overflow test - all the shifted-out bits must be same as the sign bit. + __ movl(EBX, EAX); + __ shll(EAX, ECX); + __ sarl(EAX, ECX); + __ cmpl(EAX, EBX); + __ j(NOT_EQUAL, &overflow, Assembler::kNearJump); + + __ shll(EAX, ECX); // Shift for result now we know there is no overflow. + + // EAX is a correctly tagged Smi. + __ ret(); + + __ Bind(&overflow); + // Arguments are Smi but the shift produced an overflow to Mint. + __ cmpl(EBX, Immediate(0)); + // TODO(srdjan): Implement negative values, for now fall through. + __ j(LESS, &fall_through, Assembler::kNearJump); + __ SmiUntag(EBX); + __ movl(EAX, EBX); + __ shll(EBX, ECX); + __ xorl(EDI, EDI); + __ shld(EDI, EAX); + // Result in EDI (high) and EBX (low). + const Class& mint_class = Class::ZoneHandle( + Isolate::Current()->object_store()->mint_class()); + __ LoadObject(ECX, mint_class); + AssemblerMacros::TryAllocate(assembler, + mint_class, + ECX, // Class register. + &fall_through, + EAX); // Result register. + __ movl(FieldAddress(EAX, Mint::value_offset()), EBX); + __ movl(FieldAddress(EAX, Mint::value_offset() + kWordSize), EDI); + __ ret(); + __ Bind(&fall_through); + return false; +} + + +static bool CompareIntegers(Assembler* assembler, Condition true_condition) { + Label fall_through, true_label; + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + const Bool& bool_false = Bool::ZoneHandle(Bool::False()); + TestBothArgumentsSmis(assembler, &fall_through); + // EAX contains the right argument. + __ cmpl(Address(ESP, + 2 * kWordSize), EAX); + __ j(true_condition, &true_label, Assembler::kNearJump); + __ LoadObject(EAX, bool_false); + __ ret(); + __ Bind(&true_label); + __ LoadObject(EAX, bool_true); + __ ret(); + __ Bind(&fall_through); + return false; +} + + +static bool Integer_lessThan(Assembler* assembler) { + return CompareIntegers(assembler, LESS); +} + + +static bool Integer_greaterThan(Assembler* assembler) { + return CompareIntegers(assembler, GREATER); +} + + +static bool Integer_lessEqualThan(Assembler* assembler) { + return CompareIntegers(assembler, LESS_EQUAL); +} + + +static bool Integer_greaterEqualThan(Assembler* assembler) { + return CompareIntegers(assembler, GREATER_EQUAL); +} + + +// This is called for Smi, Mint and Bigint receivers. Bigints are not handled. +static bool Integer_equalToInteger(Assembler* assembler) { + Label fall_through, true_label, check_for_mint; + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + const Bool& bool_false = Bool::ZoneHandle(Bool::False()); + // For integer receiver '===' check first. + __ movl(EAX, Address(ESP, + 1 * kWordSize)); + __ cmpl(EAX, Address(ESP, + 2 * kWordSize)); + __ j(EQUAL, &true_label, Assembler::kNearJump); + __ movl(EBX, Address(ESP, + 2 * kWordSize)); + __ orl(EAX, EBX); + __ testl(EAX, Immediate(kSmiTagMask)); + __ j(NOT_ZERO, &check_for_mint, Assembler::kNearJump); + // Both arguments are smi, '===' is good enough. + __ LoadObject(EAX, bool_false); + __ ret(); + __ Bind(&true_label); + __ LoadObject(EAX, bool_true); + __ ret(); + + // At least one of the arguments was not Smi, inline code for Smi/Mint + // equality comparison. + ObjectStore* object_store = Isolate::Current()->object_store(); + Label receiver_not_smi; + __ Bind(&check_for_mint); + __ movl(EAX, Address(ESP, + 2 * kWordSize)); // Receiver. + __ testl(EAX, Immediate(kSmiTagMask)); + __ j(NOT_ZERO, &receiver_not_smi); + + // Note that an instance of Mint never contains a value that can be + // represented by Smi. + // Left is Smi, return false if right is Mint, otherwise fall through. + __ movl(EAX, Address(ESP, + 1 * kWordSize)); // Right argument. + __ movl(EAX, FieldAddress(EAX, Object::class_offset())); + __ CompareObject(EAX, Class::ZoneHandle(object_store->mint_class())); + __ j(NOT_EQUAL, &fall_through); + __ LoadObject(EAX, bool_false); // Smi == Mint -> false. + __ ret(); + + __ Bind(&receiver_not_smi); + // EAX:: receiver. + __ movl(EAX, FieldAddress(EAX, Object::class_offset())); + __ CompareObject(EAX, Class::ZoneHandle(object_store->mint_class())); + __ j(NOT_EQUAL, &fall_through); + // Receiver is Mint, return false if right is Smi. + __ movl(EAX, Address(ESP, + 1 * kWordSize)); // Right argument. + __ testl(EAX, Immediate(kSmiTagMask)); + __ j(NOT_ZERO, &fall_through); + __ LoadObject(EAX, bool_false); // Smi == Mint -> false. + __ ret(); + // TODO(srdjan): Implement Mint == Mint comparison. + + __ Bind(&fall_through); + return false; +} + + +static bool Integer_sar(Assembler* assembler) { + Label fall_through, shift_count_ok; + TestBothArgumentsSmis(assembler, &fall_through); + // Can destroy ECX since we are not falling through. + Immediate count_limit = Immediate(0x1F); + // Check that the count is not larger than what the hardware can handle. + // For shifting right a Smi the result is the same for all numbers + // >= count_limit. + __ SmiUntag(EAX); + __ cmpl(EAX, count_limit); + __ j(LESS_EQUAL, &shift_count_ok, Assembler::kNearJump); + __ movl(EAX, count_limit); + __ Bind(&shift_count_ok); + __ movl(ECX, EAX); // Shift amount must be in ECX. + __ movl(EAX, Address(ESP, + 2 * kWordSize)); // Value. + __ SmiUntag(EAX); // Value. + __ sarl(EAX, ECX); + __ SmiTag(EAX); + __ ret(); + __ Bind(&fall_through); + return false; +} + + +static bool Smi_bitNegate(Assembler* assembler) { + Label fall_through; + __ movl(EAX, Address(ESP, + 1 * kWordSize)); // Index. + __ testl(EAX, Immediate(kSmiTagMask)); + __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); // Non-smi. + __ notl(EAX); + __ andl(EAX, Immediate(~kSmiTagMask)); // Remove inverted smi-tag. + __ ret(); + __ Bind(&fall_through); + return false; +} + + +// Check if the last argument is a double, otherwise jumps to Label +// 'not_double'. Returns the last argument in EAX. +static void TestLastArgumentsIsDouble(Assembler* assembler, Label* not_double) { + __ movl(EAX, Address(ESP, + 1 * kWordSize)); + __ testl(EAX, Immediate(kSmiTagMask)); + __ j(ZERO, not_double, Assembler::kNearJump); // Jump if Smi. + __ LoadObject(EBX, Class::ZoneHandle( + Isolate::Current()->object_store()->double_class())); + __ cmpl(EBX, FieldAddress(EAX, Object::class_offset())); + __ j(NOT_EQUAL, not_double, Assembler::kNearJump); // Jump if not double. + // Fall through if double. +} + + +// Both arguments on stack, arg0 (left) is a double, arg1 (right) is of unknown +// type. Return true or false object in the register EAX. Any NaN argument +// returns false. Any non-double arg1 causes control flow to fall through to the +// slow case (compiled method body). +static bool CompareDoubles(Assembler* assembler, Condition true_condition) { + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + const Bool& bool_false = Bool::ZoneHandle(Bool::False()); + Label fall_through, is_false, is_true; + TestLastArgumentsIsDouble(assembler, &fall_through); + // Both arguments are double, right operand is in EAX. + __ movsd(XMM1, FieldAddress(EAX, Double::value_offset())); + __ movl(EAX, Address(ESP, + 2 * kWordSize)); // Left argument. + __ movsd(XMM0, FieldAddress(EAX, Double::value_offset())); + __ comisd(XMM0, XMM1); + __ j(PARITY_EVEN, &is_false, Assembler::kNearJump); // NaN -> false; + __ j(true_condition, &is_true, Assembler::kNearJump); + // Fall through false. + __ Bind(&is_false); + __ LoadObject(EAX, bool_false); + __ ret(); + __ Bind(&is_true); + __ LoadObject(EAX, bool_true); + __ ret(); + __ Bind(&fall_through); + return false; +} + + +// arg0 is Double, arg1 is unknown. +static bool Double_greaterThan(Assembler* assembler) { + return CompareDoubles(assembler, ABOVE); +} + + +// arg0 is Double, arg1 is unknown. +static bool Double_greaterEqualThan(Assembler* assembler) { + return CompareDoubles(assembler, ABOVE_EQUAL); +} + + +// arg0 is Double, arg1 is unknown. +static bool Double_lessThan(Assembler* assembler) { + return CompareDoubles(assembler, BELOW); +} + + +// arg0 is Double, arg1 is unknown. +static bool Double_equal(Assembler* assembler) { + return CompareDoubles(assembler, EQUAL); +} + + +// arg0 is Double, arg1 is unknown. +static bool Double_lessEqualThan(Assembler* assembler) { + return CompareDoubles(assembler, BELOW_EQUAL); +} + + +static bool Double_toDouble(Assembler* assembler) { + __ movl(EAX, Address(ESP, + 1 * kWordSize)); + __ ret(); + return true; +} + + +// Expects EAX to contain right argument, left argument is on stack. Left +// argument is double, right argument is of unknown type. +static bool DoubleArithmeticOperations(Assembler* assembler, Token::Kind kind) { + Label fall_through; + TestLastArgumentsIsDouble(assembler, &fall_through); + // Both arguments are double, right operand is in EAX, class in EBX. + __ movsd(XMM1, FieldAddress(EAX, Double::value_offset())); + __ movl(EAX, Address(ESP, + 2 * kWordSize)); // Left argument. + __ movsd(XMM0, FieldAddress(EAX, Double::value_offset())); + switch (kind) { + case Token::kADD: __ addsd(XMM0, XMM1); break; + case Token::kSUB: __ subsd(XMM0, XMM1); break; + case Token::kMUL: __ mulsd(XMM0, XMM1); break; + case Token::kDIV: __ divsd(XMM0, XMM1); break; + default: UNREACHABLE(); + } + const Class& double_class = Class::ZoneHandle( + Isolate::Current()->object_store()->double_class()); + AssemblerMacros::TryAllocate(assembler, + double_class, + EBX, // Class register. + &fall_through, + EAX); // Result register. + __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0); + __ ret(); + __ Bind(&fall_through); + return false; +} + + +static bool Double_add(Assembler* assembler) { + return DoubleArithmeticOperations(assembler, Token::kADD); +} + + +static bool Double_mul(Assembler* assembler) { + return DoubleArithmeticOperations(assembler, Token::kMUL); +} + + +static bool Double_sub(Assembler* assembler) { + return DoubleArithmeticOperations(assembler, Token::kSUB); +} + + +static bool Double_div(Assembler* assembler) { + return DoubleArithmeticOperations(assembler, Token::kDIV); +} + + +// Left is double right is integer (bigint or Smi) +static bool Double_mulFromInteger(Assembler* assembler) { + Label fall_through; + // Only Smi-s allowed. + __ movl(EAX, Address(ESP, + 1 * kWordSize)); + __ testl(EAX, Immediate(kSmiTagMask)); + __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); + // Is Smi. + __ SmiUntag(EAX); + __ cvtsi2sd(XMM1, EAX); + __ movl(EAX, Address(ESP, + 2 * kWordSize)); + __ movsd(XMM0, FieldAddress(EAX, Double::value_offset())); + __ mulsd(XMM0, XMM1); + const Class& double_class = Class::ZoneHandle( + Isolate::Current()->object_store()->double_class()); + __ LoadObject(EBX, double_class); + AssemblerMacros::TryAllocate(assembler, + double_class, + EBX, // Class register. + &fall_through, + EAX); // Result register. + __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0); + __ ret(); + __ Bind(&fall_through); + return false; +} + + +// Argument type is not known +static bool Math_sqrt(Assembler* assembler) { + Label fall_through; + TestLastArgumentsIsDouble(assembler, &fall_through); + // Argument is double and is in EAX, class in EBX. + __ movsd(XMM1, FieldAddress(EAX, Double::value_offset())); + __ sqrtsd(XMM0, XMM1); + const Class& double_class = Class::ZoneHandle( + Isolate::Current()->object_store()->double_class()); + AssemblerMacros::TryAllocate(assembler, + double_class, + EBX, // Class register. + &fall_through, + EAX); // Result register. + __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0); + __ ret(); + __ Bind(&fall_through); + return false; +} + + +// Identity comparison. +static bool Object_equal(Assembler* assembler) { + Label is_true; + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + const Bool& bool_false = Bool::ZoneHandle(Bool::False()); + __ movl(EAX, Address(ESP, + 1 * kWordSize)); + __ cmpl(EAX, Address(ESP, + 2 * kWordSize)); + __ j(EQUAL, &is_true, Assembler::kNearJump); + __ LoadObject(EAX, bool_false); + __ ret(); + __ Bind(&is_true); + __ LoadObject(EAX, bool_true); + __ ret(); + return true; +} + + +static const char* kFixedSizeArrayIteratorClassName = "FixedSizeArrayIterator"; + + +// Class 'FixedSizeArrayIterator': +// T next() { +// return _array[_pos++]; +// } +// Intrinsify: return _array[_pos++]; +// TODO(srdjan): Throw a 'NoMoreElementsException' exception if the iterator +// has no more elements. +static bool FixedSizeArrayIterator_next(Assembler* assembler) { + Label fall_through; + intptr_t array_offset = + GetOffsetForField(kFixedSizeArrayIteratorClassName, "_array"); + intptr_t pos_offset = + GetOffsetForField(kFixedSizeArrayIteratorClassName, "_pos"); + ASSERT(array_offset >= 0 && pos_offset >= 0); + // Receiver is not NULL. + __ movl(EAX, Address(ESP, + 1 * kWordSize)); // Receiver. + __ movl(EBX, FieldAddress(EAX, pos_offset)); // Field _pos. + // '_pos' cannot be greater than array length and therefore is always Smi. +#if defined(DEBUG) + Label pos_ok; + __ testl(EBX, Immediate(kSmiTagMask)); + __ j(ZERO, &pos_ok, Assembler::kNearJump); + __ Stop("pos must be Smi"); + __ Bind(&pos_ok); +#endif + // Check that we are not trying to call 'next' when 'hasNext' is false. + __ movl(EAX, FieldAddress(EAX, array_offset)); // Field _array. + __ cmpl(EBX, FieldAddress(EAX, Array::length_offset())); // Range check. + __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump); + + // EBX is Smi, i.e, times 2. + ASSERT(kSmiTagShift == 1); + __ movl(EDI, FieldAddress(EAX, EBX, TIMES_2, sizeof(RawArray))); // Result. + const Immediate value = Immediate(reinterpret_cast(Smi::New(1))); + __ addl(EBX, value); // _pos++. + __ j(OVERFLOW, &fall_through, Assembler::kNearJump); + __ movl(EAX, Address(ESP, + 1 * kWordSize)); // Receiver. + __ movl(FieldAddress(EAX, pos_offset), EBX); // Store _pos. + __ movl(EAX, EDI); + __ ret(); + __ Bind(&fall_through); + return false; +} + + +// Class 'FixedSizeArrayIterator': +// bool hasNext() { +// return _length > _pos; +// } +static bool FixedSizeArrayIterator_hasNext(Assembler* assembler) { + Label fall_through, is_true; + const Bool& bool_true = Bool::ZoneHandle(Bool::True()); + const Bool& bool_false = Bool::ZoneHandle(Bool::False()); + intptr_t length_offset = + GetOffsetForField(kFixedSizeArrayIteratorClassName, "_length"); + intptr_t pos_offset = + GetOffsetForField(kFixedSizeArrayIteratorClassName, "_pos"); + __ movl(EAX, Address(ESP, + 1 * kWordSize)); // Receiver. + __ movl(EBX, FieldAddress(EAX, length_offset)); // Field _length. + __ movl(EAX, FieldAddress(EAX, pos_offset)); // Field _pos. + __ movl(EDI, EAX); + __ orl(EDI, EBX); + __ testl(EDI, Immediate(kSmiTagMask)); + __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); // Non-smi _length. + __ cmpl(EBX, EAX); // _length > _pos. + __ j(GREATER, &is_true, Assembler::kNearJump); + __ LoadObject(EAX, bool_false); + __ ret(); + __ Bind(&is_true); + __ LoadObject(EAX, bool_true); + __ ret(); + __ Bind(&fall_through); + return false; +} + + +#undef __ + + +bool Intrinsifier::Intrinsify(const Function& function, Assembler* assembler) { + if (!FLAG_intrinsify) return false; + const char* function_name = String::Handle(function.name()).ToCString(); + const Class& function_class = Class::Handle(function.owner()); + const char* class_name = String::Handle(function_class.Name()).ToCString(); +#define FIND_INTRINSICS(test_class_name, test_function_name, destination) \ + if ((strcmp(#test_function_name, function_name) == 0) && \ + (strcmp(#test_class_name, class_name) == 0)) { \ + return destination(assembler); \ + } \ + +INTRINSIC_LIST(FIND_INTRINSICS); +#undef FIND_INTRINSICS + return false; +} + +} // namespace dart + +#endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/intrinsifier_x64.cc b/runtime/vm/intrinsifier_x64.cc new file mode 100644 index 00000000000..3d325e894fc --- /dev/null +++ b/runtime/vm/intrinsifier_x64.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_X64. +#if defined(TARGET_ARCH_X64) + +#include "vm/intrinsifier.h" + +namespace dart { + +bool Intrinsifier::Intrinsify(const Function& function, Assembler* assembler) { + return false; +} + +} // namespace dart + +#endif // defined TARGET_ARCH_X64 diff --git a/runtime/vm/inttypes_support_win.h b/runtime/vm/inttypes_support_win.h new file mode 100644 index 00000000000..6dc9b46d26f --- /dev/null +++ b/runtime/vm/inttypes_support_win.h @@ -0,0 +1,17 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_INTTYPES_SUPPORT_WIN_H_ +#define VM_INTTYPES_SUPPORT_WIN_H_ + +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; +typedef signed __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +#endif // VM_INTTYPES_SUPPORT_WIN_H_ diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc new file mode 100644 index 00000000000..47ecb872766 --- /dev/null +++ b/runtime/vm/isolate.cc @@ -0,0 +1,250 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/isolate.h" + +#include "include/dart_api.h" + +#include "vm/assert.h" +#include "vm/bigint_store.h" +#include "vm/code_index_table.h" +#include "vm/compiler_stats.h" +#include "vm/dart_api_state.h" +#include "vm/debuginfo.h" +#include "vm/heap.h" +#include "vm/object_store.h" +#include "vm/parser.h" +#include "vm/port.h" +#include "vm/random.h" +#include "vm/stack_frame.h" +#include "vm/stub_code.h" +#include "vm/thread.h" +#include "vm/timer.h" +#include "vm/visitor.h" + +namespace dart { + +DEFINE_FLAG(bool, report_invocation_count, false, + "Count function invocations and report."); +DECLARE_FLAG(bool, generate_gdb_symbols); + + +Isolate::Isolate() + : store_buffer_(), + monitor_(NULL), + message_queue_(NULL), + active_ports_(0), + heap_(NULL), + object_store_(NULL), + top_resource_(NULL), + top_context_(Context::null()), + current_zone_(NULL), +#if defined(DEBUG) + no_gc_scope_depth_(0), + no_handle_scope_depth_(0), + top_handle_scope_(NULL), +#endif + random_seed_(Random::kDefaultRandomSeed), + bigint_store_(NULL), + top_exit_frame_info_(0), + init_callback_data_(NULL), + library_tag_handler_(NULL), + api_state_(NULL), + stub_code_(NULL), + code_index_table_(NULL), + long_jump_base_(NULL), + timer_list_(), + stack_limit_(0), + stack_limit_on_overflow_exception_(0), + ast_node_id_(AstNode::kInvalidId) { +} + + +Isolate::~Isolate() { + delete monitor_; + delete message_queue_; + delete heap_; + delete object_store_; + // Do not delete stack resources: top_resource_ and current_zone_. + delete bigint_store_; + delete api_state_; + delete stub_code_; + delete code_index_table_; +} + + +Isolate* Isolate::Init() { + Isolate* result = new Isolate(); + ASSERT(result != NULL); + + // TODO(5411455): For now just set the recently created isolate as + // the current isolate. + SetCurrent(result); + + // Setup the isolate monitor. + Monitor* monitor = new Monitor(); + ASSERT(monitor != NULL); + result->set_monitor(monitor); + + MessageQueue* queue = new MessageQueue(); + ASSERT(queue != NULL); + result->set_message_queue(queue); + + // Setup the Dart API state. + ApiState* state = new ApiState(); + ASSERT(state != NULL); + result->set_api_state(state); + + // Initialize stack top and limit in case we are running the isolate in the + // main thread. + // TODO(5411455): Need to figure out how to set the stack limit for the + // main thread. + result->SetStackLimitFromCurrentTOS(reinterpret_cast(&result)); + + return result; +} + + +// TODO(5411455): Use flag to override default value and Validate the +// stack size by querying OS. +uword Isolate::GetSpecifiedStackSize() { + uword stack_size = Isolate::kDefaultStackSize - Isolate::kStackSizeBuffer; + return stack_size; +} + + +void Isolate::SetStackLimitFromCurrentTOS(uword stack_top_value) { + SetStackLimit(stack_top_value - GetSpecifiedStackSize()); +} + + +void Isolate::SetStackLimit(uword limit) { + stack_limit_ = limit; + stack_limit_on_overflow_exception_ = limit - kStackSizeBuffer; +} + + +static int MostCalledFunctionFirst(const Function* const* a, + const Function* const* b) { + if ((*a)->invocation_counter() > (*b)->invocation_counter()) { + return -1; + } else if ((*a)->invocation_counter() < (*b)->invocation_counter()) { + return 1; + } else { + return 0; + } +} + + +void Isolate::PrintInvokedFunctions() { + Zone zone; + HandleScope handle_scope; + Library& library = Library::Handle(); + library = object_store()->registered_libraries(); + GrowableArray invoked_functions; + while (!library.IsNull()) { + Class& cls = Class::Handle(); + ClassDictionaryIterator iter(library); + while (iter.HasNext()) { + cls = iter.GetNext(); + const Array& functions = Array::Handle(cls.functions()); + for (int j = 0; j < functions.Length(); j++) { + Function& function = Function::Handle(); + function ^= functions.At(j); + if (function.invocation_counter() > 0) { + invoked_functions.Add(&function); + } + } + } + library = library.next_registered(); + } + invoked_functions.Sort(MostCalledFunctionFirst); + for (int i = 0; i < invoked_functions.length(); i++) { + OS::Print("%10d x %s\n", + invoked_functions[i]->invocation_counter(), + invoked_functions[i]->ToFullyQualifiedCString()); + } +} + + +void Isolate::Shutdown() { + ASSERT(this == Isolate::Current()); + ASSERT(top_resource_ == NULL); + ASSERT((heap_ == NULL) || heap_->Verify()); + + // Close all the ports owned by this isolate. + PortMap::ClosePorts(); + + delete message_queue(); + set_message_queue(NULL); + + // Remove the monitor associated with this isolate. + delete monitor(); + set_monitor(NULL); + + // Dump all accumalated timer data for the isolate. + timer_list_.ReportTimers(); + if (FLAG_report_invocation_count) { + PrintInvokedFunctions(); + } + CompilerStats::Print(); + if (FLAG_generate_gdb_symbols) { + DebugInfo::UnregisterAllSections(); + } + + // TODO(5411455): For now just make sure there are no current isolates + // as we are shutting down the isolate. + SetCurrent(NULL); +} + + +Dart_IsolateInitCallback Isolate::init_callback_ = NULL; + + +void Isolate::SetInitCallback(Dart_IsolateInitCallback callback) { + init_callback_ = callback; +} + + +Dart_IsolateInitCallback Isolate::InitCallback() { + return init_callback_; +} + + +void Isolate::VisitObjectPointers(ObjectPointerVisitor* visitor, + bool validate_frames) { + ASSERT(visitor != NULL); + + // Visit objects in the object store. + object_store()->VisitObjectPointers(visitor); + + // Visit objects in per isolate stubs. + StubCode::VisitObjectPointers(visitor); + + // Visit objects in zones. + current_zone()->VisitObjectPointers(visitor); + + // Iterate over all the stack frames and visit objects on the stack. + StackFrameIterator frames_iterator(validate_frames); + StackFrame* frame = frames_iterator.NextFrame(); + while (frame != NULL) { + frame->VisitObjectPointers(visitor); + frame = frames_iterator.NextFrame(); + } + + // Visit the dart api state for all local and persistent handles. + if (api_state() != NULL) { + api_state()->VisitObjectPointers(visitor); + } + + // Visit all objects in the code index table. + if (code_index_table() != NULL) { + code_index_table()->VisitObjectPointers(visitor); + } + + // Visit the top context which is stored in the isolate. + visitor->VisitPointer(reinterpret_cast(&top_context_)); +} + +} // namespace dart diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h new file mode 100644 index 00000000000..ee8da55845b --- /dev/null +++ b/runtime/vm/isolate.h @@ -0,0 +1,270 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_ISOLATE_H_ +#define VM_ISOLATE_H_ + +#include + +#include "include/dart_api.h" +#include "vm/assert.h" +#include "vm/store_buffer.h" +#include "vm/timer.h" + +namespace dart { + +// Forward declarations. +class ApiState; +class BigintStore; +class CodeIndexTable; +class HandleScope; +class Heap; +class LongJump; +class MessageQueue; +class Monitor; +class ObjectPointerVisitor; +class ObjectStore; +class RawContext; +class StackResource; +class StubCode; +class Zone; + +class Isolate { + public: + ~Isolate(); + + static inline Isolate* Current(); + static void SetCurrent(Isolate* isolate); + + static void InitOnce(); + static Isolate* Init(); + void Shutdown(); + + // Visit all object pointers. + void VisitObjectPointers(ObjectPointerVisitor* visitor, bool validate_frames); + + StoreBufferBlock* store_buffer() { return &store_buffer_; } + + Monitor* monitor() const { return monitor_; } + void set_monitor(Monitor* value) { monitor_ = value; } + + MessageQueue* message_queue() const { return message_queue_; } + void set_message_queue(MessageQueue* value) { message_queue_ = value; } + + // The count of active ports is only correct when read from the current + // isolate. This value is not protected from being updated concurrently. + intptr_t active_ports() const { return active_ports_; } + void increment_active_ports() { + ASSERT(this == Isolate::Current()); + active_ports_++; + } + void decrement_active_ports() { + ASSERT(this == Isolate::Current()); + active_ports_--; + } + + Heap* heap() const { return heap_; } + void set_heap(Heap* value) { heap_ = value; } + static intptr_t heap_offset() { return OFFSET_OF(Isolate, heap_); } + + ObjectStore* object_store() const { return object_store_; } + void set_object_store(ObjectStore* value) { object_store_ = value; } + static intptr_t object_store_offset() { + return OFFSET_OF(Isolate, object_store_); + } + + StackResource* top_resource() const { return top_resource_; } + void set_top_resource(StackResource* value) { top_resource_ = value; } + + RawContext* top_context() const { return top_context_; } + void set_top_context(RawContext* value) { top_context_ = value; } + static intptr_t top_context_offset() { + return OFFSET_OF(Isolate, top_context_); + } + + int32_t random_seed() const { return random_seed_; } + void set_random_seed(int32_t value) { random_seed_ = value; } + + uword top_exit_frame_info() const { return top_exit_frame_info_; } + void set_top_exit_frame_info(uword value) { top_exit_frame_info_ = value; } + static intptr_t top_exit_frame_info_offset() { + return OFFSET_OF(Isolate, top_exit_frame_info_); + } + + ApiState* api_state() const { return api_state_; } + void set_api_state(ApiState* value) { api_state_ = value; } + + StubCode* stub_code() const { return stub_code_; } + void set_stub_code(StubCode* value) { stub_code_ = value; } + + CodeIndexTable* code_index_table() const { return code_index_table_; } + void set_code_index_table(CodeIndexTable* value) { + code_index_table_ = value; + } + + LongJump* long_jump_base() const { return long_jump_base_; } + void set_long_jump_base(LongJump* value) { long_jump_base_ = value; } + + TimerList& timer_list() { return timer_list_; } + + Zone* current_zone() const { return current_zone_; } + void set_current_zone(Zone* zone) { current_zone_ = zone; } + static intptr_t current_zone_offset() { + return OFFSET_OF(Isolate, current_zone_); + } + + BigintStore* bigint_store() const { return bigint_store_; } + void set_bigint_store(BigintStore* store) { bigint_store_ = store; } + + int32_t no_gc_scope_depth() const { +#if defined(DEBUG) + return no_gc_scope_depth_; +#else + return 0; +#endif + } + + void IncrementNoGCScopeDepth() { +#if defined(DEBUG) + ASSERT(no_gc_scope_depth_ < INT_MAX); + no_gc_scope_depth_ += 1; +#endif + } + + void DecrementNoGCScopeDepth() { +#if defined(DEBUG) + ASSERT(no_gc_scope_depth_ > 0); + no_gc_scope_depth_ -= 1; +#endif + } + + int32_t no_handle_scope_depth() const { +#if defined(DEBUG) + return no_handle_scope_depth_; +#else + return 0; +#endif + } + + void IncrementNoHandleScopeDepth() { +#if defined(DEBUG) + ASSERT(no_handle_scope_depth_ < INT_MAX); + no_handle_scope_depth_ += 1; +#endif + } + + void DecrementNoHandleScopeDepth() { +#if defined(DEBUG) + ASSERT(no_handle_scope_depth_ > 0); + no_handle_scope_depth_ -= 1; +#endif + } + + HandleScope* top_handle_scope() const { +#if defined(DEBUG) + return top_handle_scope_; +#else + return 0; +#endif + } + + void set_top_handle_scope(HandleScope* handle_scope) { +#if defined(DEBUG) + top_handle_scope_ = handle_scope; +#endif + } + + void set_init_callback_data(void* value) { + init_callback_data_ = value; + } + void* init_callback_data() const { + return init_callback_data_; + } + + Dart_LibraryTagHandler library_tag_handler() const { + return library_tag_handler_; + } + void set_library_tag_handler(Dart_LibraryTagHandler value) { + library_tag_handler_ = value; + } + + static void SetInitCallback(Dart_IsolateInitCallback callback); + static Dart_IsolateInitCallback InitCallback(); + + uword stack_limit_address() const { + return reinterpret_cast(&stack_limit_); + } + + void SetStackLimit(uword value); + uword stack_limit() const { return stack_limit_; } + + void SetStackLimitFromCurrentTOS(uword isolate_stack_top); + + void ResetStackLimitAfterException() { + stack_limit_ = stack_limit_on_overflow_exception_ + kStackSizeBuffer; + } + + void AdjustStackLimitForException() { + stack_limit_ = stack_limit_on_overflow_exception_; + } + + intptr_t ast_node_id() const { return ast_node_id_; } + void set_ast_node_id(int value) { ast_node_id_ = value; } + + private: + Isolate(); + + void PrintInvokedFunctions(); + + static uword GetSpecifiedStackSize(); + + static const uword kStackSizeBuffer = (128 * KB); + static const uword kDefaultStackSize = (1 * MB); + + StoreBufferBlock store_buffer_; + Monitor* monitor_; + MessageQueue* message_queue_; + intptr_t active_ports_; + Heap* heap_; + ObjectStore* object_store_; + StackResource* top_resource_; + RawContext* top_context_; + Zone* current_zone_; +#if defined(DEBUG) + int32_t no_gc_scope_depth_; + int32_t no_handle_scope_depth_; + HandleScope* top_handle_scope_; +#endif + int32_t random_seed_; + BigintStore* bigint_store_; + uword top_exit_frame_info_; + void* init_callback_data_; + Dart_LibraryTagHandler library_tag_handler_; + ApiState* api_state_; + StubCode* stub_code_; + CodeIndexTable* code_index_table_; + LongJump* long_jump_base_; + TimerList timer_list_; + uword stack_limit_; + uword stack_limit_on_overflow_exception_; + intptr_t ast_node_id_; + + static Dart_IsolateInitCallback init_callback_; + + DISALLOW_COPY_AND_ASSIGN(Isolate); +}; + +} // namespace dart + +#if defined(TARGET_OS_LINUX) +#include "vm/isolate_linux.h" +#elif defined(TARGET_OS_MACOS) +#include "vm/isolate_macos.h" +#elif defined(TARGET_OS_WINDOWS) +#include "vm/isolate_win.h" +#else +#error Unknown target os. +#endif + +#endif // VM_ISOLATE_H_ diff --git a/runtime/vm/isolate_linux.cc b/runtime/vm/isolate_linux.cc new file mode 100644 index 00000000000..fc041ee15ee --- /dev/null +++ b/runtime/vm/isolate_linux.cc @@ -0,0 +1,47 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include + +#include "vm/assert.h" +#include "vm/isolate.h" + +namespace dart { + +#define VALIDATE_PTHREAD_RESULT(result) \ + if (result != 0) { \ + FATAL2("pthread error: %d (%s)", result, strerror(result)); \ + } + + +// The single pthread key which stores all the thread local data for a +// thread. Since an Isolate is the central repository for storing all +// isolate specific information a single pthread key is sufficient. +pthread_key_t isolate_key = PTHREAD_KEY_UNSET; + + +void Isolate::SetCurrent(Isolate* current) { + ASSERT(isolate_key != PTHREAD_KEY_UNSET); + int result = pthread_setspecific(isolate_key, current); + VALIDATE_PTHREAD_RESULT(result); +} + + +// Empty isolate init callback which is registered before VM isolate creation. +static void* VMIsolateInitCallback(void* data) { + return reinterpret_cast(1); +} + + +void Isolate::InitOnce() { + ASSERT(isolate_key == PTHREAD_KEY_UNSET); + int result = pthread_key_create(&isolate_key, NULL); + // Make sure creating a key was successful. + VALIDATE_PTHREAD_RESULT(result); + ASSERT(isolate_key != PTHREAD_KEY_UNSET); + init_callback_ = VMIsolateInitCallback; +} + +} // namespace dart diff --git a/runtime/vm/isolate_linux.h b/runtime/vm/isolate_linux.h new file mode 100644 index 00000000000..46c4f57c841 --- /dev/null +++ b/runtime/vm/isolate_linux.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_ISOLATE_LINUX_H_ +#define VM_ISOLATE_LINUX_H_ + +#if !defined(VM_ISOLATE_H_) +#error Do not include isolate_linux.h directly; use isolate.h instead. +#endif + +#include + +namespace dart { + +#define PTHREAD_KEY_UNSET static_cast(-1) +extern pthread_key_t isolate_key; + + +inline Isolate* Isolate::Current() { + ASSERT(isolate_key != PTHREAD_KEY_UNSET); + return reinterpret_cast(pthread_getspecific(isolate_key)); +} + +} // namespace dart + +#endif // VM_ISOLATE_LINUX_H_ diff --git a/runtime/vm/isolate_macos.cc b/runtime/vm/isolate_macos.cc new file mode 100644 index 00000000000..090a102f7c0 --- /dev/null +++ b/runtime/vm/isolate_macos.cc @@ -0,0 +1,47 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include + +#include "vm/assert.h" +#include "vm/isolate.h" + +namespace dart { + +#define VALIDATE_PTHREAD_RESULT(result) \ + if (result != 0) { \ + FATAL2("pthread error: %d (%s)", result, strerror(result)); \ + } + + +// The single pthread key which stores all the thread local data for a +// thread. Since an Isolate is the central repository for storing all +// isolate specific information a single pthread key is sufficient. +pthread_key_t isolate_key = PTHREAD_KEY_UNSET; + + +void Isolate::SetCurrent(Isolate* current) { + ASSERT(isolate_key != PTHREAD_KEY_UNSET); + int result = pthread_setspecific(isolate_key, current); + VALIDATE_PTHREAD_RESULT(result); +} + + +// Empty isolate init callback which is registered before VM isolate creation. +static void* VMIsolateInitCallback(void* data) { + return reinterpret_cast(1); +} + + +void Isolate::InitOnce() { + ASSERT(isolate_key == PTHREAD_KEY_UNSET); + int result = pthread_key_create(&isolate_key, NULL); + // Make sure creating a key was successful. + VALIDATE_PTHREAD_RESULT(result); + ASSERT(isolate_key != PTHREAD_KEY_UNSET); + init_callback_ = VMIsolateInitCallback; +} + +} // namespace dart diff --git a/runtime/vm/isolate_macos.h b/runtime/vm/isolate_macos.h new file mode 100644 index 00000000000..961d4c271b4 --- /dev/null +++ b/runtime/vm/isolate_macos.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_ISOLATE_MACOS_H_ +#define VM_ISOLATE_MACOS_H_ + +#if !defined(VM_ISOLATE_H_) +#error Do not include isolate_macos.h directly; use isolate.h instead. +#endif + +#include + +namespace dart { + +#define PTHREAD_KEY_UNSET static_cast(-1) +extern pthread_key_t isolate_key; + + +inline Isolate* Isolate::Current() { + ASSERT(isolate_key != PTHREAD_KEY_UNSET); + return reinterpret_cast(pthread_getspecific(isolate_key)); +} + +} // namespace dart + +#endif // VM_ISOLATE_MACOS_H_ diff --git a/runtime/vm/isolate_test.cc b/runtime/vm/isolate_test.cc new file mode 100644 index 00000000000..7e41e48168c --- /dev/null +++ b/runtime/vm/isolate_test.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/globals.h" +#include "vm/isolate.h" +#include "vm/unit_test.h" + +namespace dart { + +UNIT_TEST_CASE(IsolateCurrent) { + Isolate* isolate = Isolate::Init(); + EXPECT_EQ(isolate, Isolate::Current()); + isolate->Shutdown(); + EXPECT_EQ(reinterpret_cast(NULL), Isolate::Current()); + delete isolate; +} + + +#if defined(TARGET_ARCH_IA32) // only ia32 can run dart execution tests. +// Unit test case to verify error during isolate spawning (application classes +// not loaded into the isolate). +TEST_CASE(IsolateSpawn) { + const char* kScriptChars = + "class SpawnNewIsolate extends Isolate {\n" + " SpawnNewIsolate() : super() { }\n" + " void main() {\n" + " }\n" + " static int testMain() {\n" + " try {\n" + " new SpawnNewIsolate().spawn().then(function(SendPort port) {\n" + " });\n" + " } catch (var e) {\n" + " throw;\n" + " }\n" + " return 0;\n" + " }\n" + "}\n"; + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + Dart_Result result = Dart_InvokeStatic(lib, + Dart_NewString("SpawnNewIsolate"), + Dart_NewString("testMain"), + 0, + NULL); + EXPECT(Dart_IsValidResult(result)); + Dart_Handle result_obj = Dart_GetResult(result); + EXPECT(Dart_ExceptionOccurred(result_obj)); + Dart_Result exception_result = Dart_GetException(result_obj); + EXPECT(Dart_IsValidResult(exception_result)); +} +#endif // TARGET_ARCH_IA32. + +} // namespace dart diff --git a/runtime/vm/isolate_win.cc b/runtime/vm/isolate_win.cc new file mode 100644 index 00000000000..cd3cfeef5b1 --- /dev/null +++ b/runtime/vm/isolate_win.cc @@ -0,0 +1,38 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/globals.h" +#include "vm/isolate.h" + +namespace dart { + +DWORD isolate_key = TLS_OUT_OF_INDEXES; + + +void Isolate::SetCurrent(Isolate* current) { + ASSERT(isolate_key != TLS_OUT_OF_INDEXES); + BOOL result = TlsSetValue(isolate_key, current); + if (!result) { + FATAL("TlsSetValue failed"); + } +} + + +// Empty isolate init callback which is registered before VM isolate creation. +static void* VMIsolateInitCallback(void* data) { + return reinterpret_cast(1); +} + + +void Isolate::InitOnce() { + ASSERT(isolate_key == TLS_OUT_OF_INDEXES); + isolate_key = TlsAlloc(); + if (isolate_key == TLS_OUT_OF_INDEXES) { + FATAL("TlsAlloc failed"); + } + init_callback_ = VMIsolateInitCallback; +} + +} // namespace dart diff --git a/runtime/vm/isolate_win.h b/runtime/vm/isolate_win.h new file mode 100644 index 00000000000..2561746681f --- /dev/null +++ b/runtime/vm/isolate_win.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_ISOLATE_WIN_H_ +#define VM_ISOLATE_WIN_H_ + +#if !defined(VM_ISOLATE_H_) +#error Do not include isolate_win.h directly; use isolate.h instead. +#endif + +#include "vm/assert.h" +#include "vm/globals.h" + +namespace dart { + +extern DWORD isolate_key; + + +inline Isolate* Isolate::Current() { + ASSERT(isolate_key != TLS_OUT_OF_INDEXES); + return reinterpret_cast(TlsGetValue(isolate_key)); +} + +} // namespace dart + +#endif // VM_ISOLATE_WIN_H_ diff --git a/runtime/vm/longjump.cc b/runtime/vm/longjump.cc new file mode 100644 index 00000000000..742b8e9f4be --- /dev/null +++ b/runtime/vm/longjump.cc @@ -0,0 +1,43 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/longjump.h" + +#include "include/dart_api.h" + +#include "vm/dart_api_impl.h" +#include "vm/isolate.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/os.h" + +namespace dart { + +jmp_buf* LongJump::Set() { + top_ = Isolate::Current()->top_resource(); + return &environment_; +} + + +void LongJump::Jump(int value, const char* msg) { + // A zero is the default return value from setting up a LongJump using Set. + ASSERT(value != 0); + + Isolate* isolate = Isolate::Current(); + + // Remember the message in the sticky error message of this isolate. + const String& error = String::Handle(String::New(msg)); + isolate->object_store()->set_sticky_error(error); + + // Destruct all the active StackResource objects. + StackResource* current_resource = isolate->top_resource(); + while (current_resource != top_) { + current_resource->~StackResource(); + current_resource = isolate->top_resource(); + } + longjmp(environment_, value); + UNREACHABLE(); +} + +} // namespace dart diff --git a/runtime/vm/longjump.h b/runtime/vm/longjump.h new file mode 100644 index 00000000000..f41c63a14f1 --- /dev/null +++ b/runtime/vm/longjump.h @@ -0,0 +1,30 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_LONGJUMP_H_ +#define VM_LONGJUMP_H_ + +#include + +#include "vm/allocation.h" + +namespace dart { + +class LongJump : public ValueObject { + public: + LongJump() : top_(NULL) { } + + jmp_buf* Set(); + void Jump(int value, const char* msg); + + private: + jmp_buf environment_; + StackResource* top_; + + DISALLOW_COPY_AND_ASSIGN(LongJump); +}; + +} // namespace dart + +#endif // VM_LONGJUMP_H_ diff --git a/runtime/vm/longjump_test.cc b/runtime/vm/longjump_test.cc new file mode 100644 index 00000000000..eadad02000a --- /dev/null +++ b/runtime/vm/longjump_test.cc @@ -0,0 +1,25 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/isolate.h" +#include "vm/longjump.h" +#include "vm/unit_test.h" + +namespace dart { + +static void LongJumpHelper(LongJump* jump) { + jump->Jump(1, "LongJump Test"); + UNREACHABLE(); +} + + +TEST_CASE(LongJump) { + LongJump jump; + if (setjmp(*jump.Set()) == 0) { + LongJumpHelper(&jump); + UNREACHABLE(); + } +} + +} // namespace dart diff --git a/runtime/vm/memory_region.cc b/runtime/vm/memory_region.cc new file mode 100644 index 00000000000..ce0a107020b --- /dev/null +++ b/runtime/vm/memory_region.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/memory_region.h" + +namespace dart { + +void MemoryRegion::CopyFrom(uword offset, const MemoryRegion& from) const { + ASSERT(from.pointer() != NULL && from.size() > 0); + ASSERT(this->size() >= from.size()); + ASSERT(offset <= this->size() - from.size()); + memmove(reinterpret_cast(start() + offset), + from.pointer(), from.size()); +} + +} // namespace dart diff --git a/runtime/vm/memory_region.h b/runtime/vm/memory_region.h new file mode 100644 index 00000000000..9078a781388 --- /dev/null +++ b/runtime/vm/memory_region.h @@ -0,0 +1,85 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_MEMORY_REGION_H_ +#define VM_MEMORY_REGION_H_ + +#include "vm/assert.h" + +namespace dart { + +// Memory regions are useful for accessing memory with bounds check in +// debug mode. They can be safely passed by value and do not assume ownership +// of the region. +class MemoryRegion { + public: + MemoryRegion() : pointer_(NULL), size_(0) { } + MemoryRegion(void* pointer, uword size) : pointer_(pointer), size_(size) { } + + void* pointer() const { return pointer_; } + uword size() const { return size_; } + uword size_in_bits() const { return size_ * kBitsPerByte; } + + static uword pointer_offset() { return OFFSET_OF(MemoryRegion, pointer_); } + + uword start() const { return reinterpret_cast(pointer_); } + uword end() const { return start() + size_; } + + template T Load(uword offset) const { + return *ComputeInternalPointer(offset); + } + + template void Store(uword offset, T value) const { + *ComputeInternalPointer(offset) = value; + } + + template T* PointerTo(uword offset) const { + return ComputeInternalPointer(offset); + } + + bool Contains(uword address) const { + return (address >= start()) && (address < end()); + } + + void CopyFrom(uword offset, const MemoryRegion& from) const; + + // Compute a sub memory region based on an existing one. + void Subregion(const MemoryRegion& from, uword offset, uword size) { + ASSERT(from.size() >= size); + ASSERT(offset <= (from.size() - size)); + pointer_ = reinterpret_cast(from.start() + offset); + size_ = size; + } + + // Compute an extended memory region based on an existing one. + void Extend(const MemoryRegion& region, uword extra) { + pointer_ = region.pointer(); + size_ = (region.size() + extra); + } + + private: + template T* ComputeInternalPointer(uword offset) const { + ASSERT(size() >= sizeof(T)); + ASSERT(offset <= size() - sizeof(T)); + return reinterpret_cast(start() + offset); + } + + // Locate the bit with the given offset. Returns a pointer to the byte + // containing the bit, and sets bit_mask to the bit within that byte. + uint8_t* ComputeBitPointer(uword bit_offset, uint8_t* bit_mask) const { + uword bit_remainder = (bit_offset & (kBitsPerByte - 1)); + *bit_mask = (1U << bit_remainder); + uword byte_offset = (bit_offset >> kBitsPerByteLog2); + return ComputeInternalPointer(byte_offset); + } + + void* pointer_; + uword size_; + + DISALLOW_COPY_AND_ASSIGN(MemoryRegion); +}; + +} // namespace dart + +#endif // VM_MEMORY_REGION_H_ diff --git a/runtime/vm/memory_region_test.cc b/runtime/vm/memory_region_test.cc new file mode 100644 index 00000000000..0575e769f4b --- /dev/null +++ b/runtime/vm/memory_region_test.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/assert.h" +#include "vm/memory_region.h" +#include "vm/unit_test.h" + +namespace dart { + +static void* NewRegion(uword size) { + void* pointer = new uint8_t[size]; + return pointer; +} + + +static void DeleteRegion(const MemoryRegion& region) { + delete[] reinterpret_cast(region.pointer()); +} + + +UNIT_TEST_CASE(NullRegion) { + static const uword kSize = 512; + MemoryRegion region(NULL, kSize); + EXPECT(region.pointer() == NULL); + EXPECT_EQ(kSize, region.size()); +} + + +UNIT_TEST_CASE(NewRegion) { + static const uword kSize = 1024; + MemoryRegion region(NewRegion(kSize), kSize); + EXPECT_EQ(kSize, region.size()); + EXPECT(region.pointer() != NULL); + + region.Store(0, 42); + EXPECT_EQ(42, region.Load(0)); + + DeleteRegion(region); +} + + +UNIT_TEST_CASE(Subregion) { + static const uword kSize = 1024; + static const uword kSubOffset = 128; + static const uword kSubSize = 512; + MemoryRegion region(NewRegion(kSize), kSize); + MemoryRegion sub_region; + sub_region.Subregion(region, kSubOffset, kSubSize); + EXPECT_EQ(kSubSize, sub_region.size()); + EXPECT(sub_region.pointer() != NULL); + EXPECT(sub_region.start() == region.start() + kSubOffset); + + region.Store(0, 42); + sub_region.Store(0, 100); + EXPECT_EQ(42, region.Load(0)); + EXPECT_EQ(100, region.Load(kSubOffset)); + + DeleteRegion(region); +} + + +UNIT_TEST_CASE(ExtendedRegion) { + static const uword kSize = 1024; + static const uword kSubSize = 512; + static const uword kExtendSize = 512; + MemoryRegion region(NewRegion(kSize), kSize); + MemoryRegion sub_region; + sub_region.Subregion(region, 0, kSubSize); + MemoryRegion extended_region; + extended_region.Extend(sub_region, kExtendSize); + EXPECT_EQ(kSize, extended_region.size()); + EXPECT(extended_region.pointer() == region.pointer()); + EXPECT(extended_region.pointer() == sub_region.pointer()); + + extended_region.Store(0, 42); + EXPECT_EQ(42, extended_region.Load(0)); + + DeleteRegion(region); +} + +} // namespace dart diff --git a/runtime/vm/native_arguments.cc b/runtime/vm/native_arguments.cc new file mode 100644 index 00000000000..2f33efa8593 --- /dev/null +++ b/runtime/vm/native_arguments.cc @@ -0,0 +1,16 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/native_arguments.h" + +#include "vm/assert.h" +#include "vm/object.h" + +namespace dart { + +void NativeArguments::SetReturn(const Object& value) const { + *retval_ = value.raw(); +} + +} // namespace dart diff --git a/runtime/vm/native_arguments.h b/runtime/vm/native_arguments.h new file mode 100644 index 00000000000..075733c78e1 --- /dev/null +++ b/runtime/vm/native_arguments.h @@ -0,0 +1,57 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_NATIVE_ARGUMENTS_H_ +#define VM_NATIVE_ARGUMENTS_H_ + +#include "vm/assert.h" + +namespace dart { + +// Forward declarations. +class Object; +class RawObject; + + +// Class NativeArguments is used to access arguments passed in from +// generated dart code to a runtime function or a dart library native +// function. It is also used to set the return value if any at the slot +// reserved for return values. +// All runtime function/dart library native functions have the +// following signature: +// void function_name(NativeArguments arguments); +// Inside the function, arguments are accessed as follows: +// const Type& arg1 = Type::CheckedHandle(arguments.GetArgument(0)); +// const Type& arg2 = Type::CheckedHandle(arguments.GetArgument(1)); +// The return value is set as follows: +// arguments.SetReturnValue(result); +// NOTE: Since we pass this as a pass by value argument in the stubs we don't +// have DISALLOW_COPY_AND_ASSIGN in the class definition and do not make it a +// subclass of ValueObject. +class NativeArguments { + public: + int Count() const { return argc_; } + + RawObject* At(int index) const { + ASSERT(index >=0 && index < argc_); + return (*argv_)[-index]; + } + + void SetReturn(const Object& value) const; + + static intptr_t argc_offset() { return OFFSET_OF(NativeArguments, argc_); } + static intptr_t argv_offset() { return OFFSET_OF(NativeArguments, argv_); } + static intptr_t retval_offset() { + return OFFSET_OF(NativeArguments, retval_); + } + + private: + int argc_; // Number of arguments passed to the runtime call. + RawObject*(*argv_)[]; // Pointer to an array of arguments to runtime call. + RawObject** retval_; // Pointer to the return value area. +}; + +} // namespace dart + +#endif // VM_NATIVE_ARGUMENTS_H_ diff --git a/runtime/vm/native_entry.cc b/runtime/vm/native_entry.cc new file mode 100644 index 00000000000..90a92c76428 --- /dev/null +++ b/runtime/vm/native_entry.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/native_entry.h" + +#include "include/dart_api.h" + +#include "vm/dart_api_impl.h" + +namespace dart { + +DEFINE_FLAG(bool, trace_natives, false, "Trace invocation of natives"); + +NativeFunction NativeEntry::ResolveNative(const Class& cls, + const String& function_name, + int number_of_arguments) { + // Now resolve the native function to the corresponding native entrypoint. + const Library& library = Library::Handle(cls.library()); + if (library.native_entry_resolver() == 0) { + // Native methods are not allowed in the library to which this + // class belongs in. + return NULL; + } + Dart_EnterScope(); // Enter a new Dart API scope as we invoke API entries. + Dart_NativeEntryResolver resolver = library.native_entry_resolver(); + Dart_NativeFunction native_function = + resolver(Api::NewLocalHandle(function_name), number_of_arguments); + Dart_ExitScope(); // Exit the Dart API scope. + return reinterpret_cast(native_function); +} + +} // namespace dart diff --git a/runtime/vm/native_entry.h b/runtime/vm/native_entry.h new file mode 100644 index 00000000000..7500d389b4b --- /dev/null +++ b/runtime/vm/native_entry.h @@ -0,0 +1,67 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_NATIVE_ENTRY_H_ +#define VM_NATIVE_ENTRY_H_ + +#include "vm/allocation.h" +#include "vm/assembler.h" +#include "vm/native_arguments.h" +#include "vm/verifier.h" + +#include "include/dart_api.h" + +namespace dart { + +DECLARE_FLAG(bool, trace_natives); + +// Forward declarations. +class Class; +class String; + +typedef void (*NativeFunction)(NativeArguments* arguments); + + +#define NATIVE_ENTRY_FUNCTION(name) DN_##name + + +// Helper macros for declaring and defining native entries. +#define REGISTER_NATIVE_ENTRY(name, count) \ + { ""#name, NATIVE_ENTRY_FUNCTION(name), count }, + + +#define DEFINE_NATIVE_ENTRY(name, argument_count) \ + static void DN_Helper##name(NativeArguments* arguments); \ + void NATIVE_ENTRY_FUNCTION(name)(Dart_NativeArguments args) { \ + CHECK_STACK_ALIGNMENT; \ + VERIFY_ON_TRANSITION; \ + NativeArguments* arguments = reinterpret_cast(args); \ + ASSERT(arguments->Count() == argument_count); \ + if (FLAG_trace_natives) OS::Print("Calling native: %s\n", ""#name); \ + { \ + Zone zone; \ + HANDLESCOPE(); \ + DN_Helper##name(arguments); \ + } \ + VERIFY_ON_TRANSITION; \ + } \ + static void DN_Helper##name(NativeArguments* arguments) + + +#define DECLARE_NATIVE_ENTRY(name, argument_count) \ + extern void NATIVE_ENTRY_FUNCTION(name)(Dart_NativeArguments arguments); + + +// Helper class for resolving and handling native functions. +class NativeEntry : public AllStatic { + public: + // Resolve specified dart native function to the actual native entrypoint. + static NativeFunction ResolveNative(const Class& cls, + const String& function_name, + int number_of_arguments); +}; + +} // namespace dart + +#endif // VM_NATIVE_ENTRY_H_ diff --git a/runtime/vm/native_entry_test.cc b/runtime/vm/native_entry_test.cc new file mode 100644 index 00000000000..4d7bdacc304 --- /dev/null +++ b/runtime/vm/native_entry_test.cc @@ -0,0 +1,90 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/native_entry_test.h" + +#include "vm/assembler.h" +#include "vm/code_patcher.h" +#include "vm/native_entry.h" +#include "vm/object.h" +#include "vm/stack_frame.h" +#include "vm/stub_code.h" + +namespace dart { + +struct NativeTestEntries { + const char* name_; + Dart_NativeFunction function_; + int argument_count_; +} TestEntries[] = { + REGISTER_NATIVE_ENTRY(TestSmiSub, 2) + REGISTER_NATIVE_ENTRY(TestSmiSum, 6) + REGISTER_NATIVE_ENTRY(TestStaticCallPatching, 0) +}; + + +Dart_NativeFunction NativeTestEntry_Lookup(const String& name, + int argument_count) { + int num_entries = sizeof(TestEntries) / sizeof(struct NativeTestEntries); + for (int i = 0; i < num_entries; i++) { + struct NativeTestEntries* entry = &(TestEntries[i]); + if (name.Equals(entry->name_, strlen(entry->name_))) { + if (entry->argument_count_ == argument_count) { + return entry->function_; + } else { + // Wrong number of arguments. + // TODO(regis): Should we pass a buffer for error reporting? + return NULL; + } + } + } + return NULL; +} + + +// A native call for test purposes. +// Arg0: a smi. +// Arg1: a smi. +// Result: a smi representing arg0 - arg1. +DEFINE_NATIVE_ENTRY(TestSmiSub, 2) { + const Smi& left = Smi::CheckedHandle(arguments->At(0)); + const Smi& right = Smi::CheckedHandle(arguments->At(1)); + // Ignoring overflow in the calculation below. + intptr_t result = left.Value() - right.Value(); + arguments->SetReturn(Smi::Handle(Smi::New(result))); +} + + +// A native call for test purposes. +// Arg0-4: 5 smis. +// Result: a smi representing the sum of all arguments. +DEFINE_NATIVE_ENTRY(TestSmiSum, 5) { + intptr_t result = 0; + for (int i = 0; i < arguments->Count(); i++) { + const Smi& arg = Smi::CheckedHandle(arguments->At(i)); + // Ignoring overflow in the addition below. + result += arg.Value(); + } + arguments->SetReturn(Smi::Handle(Smi::New(result))); +} + + +// Test code patching. +DEFINE_NATIVE_ENTRY(TestStaticCallPatching, 0) { + uword target_address = 0; + Function& target_function = Function::Handle(); + DartFrameIterator iterator; + iterator.NextFrame(); // Skip native call. + DartFrame* static_caller_frame = iterator.NextFrame(); + CodePatcher::GetStaticCallAt(static_caller_frame->pc(), + &target_function, + &target_address); + EXPECT(String::Handle(target_function.name()). + Equals(String::Handle(String::New("NativePatchStaticCall")))); + const uword function_entry_address = + Code::Handle(target_function.code()).EntryPoint(); + EXPECT_EQ(function_entry_address, target_address); +} + +} // namespace dart diff --git a/runtime/vm/native_entry_test.h b/runtime/vm/native_entry_test.h new file mode 100644 index 00000000000..f976326a73d --- /dev/null +++ b/runtime/vm/native_entry_test.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_NATIVE_ENTRY_TEST_H_ +#define VM_NATIVE_ENTRY_TEST_H_ + +#include "vm/native_entry.h" + +#include "include/dart_api.h" + +namespace dart { + +// Forward declarations. +class String; + + +DECLARE_NATIVE_ENTRY(TestSmiSub, 2); +DECLARE_NATIVE_ENTRY(TestSmiSum, 6); +DECLARE_NATIVE_ENTRY(TestStaticCallPatching, 0); + +// Helper function for looking up native test functions. +extern Dart_NativeFunction NativeTestEntry_Lookup(const String& name, + int argument_count); + +} // namespace dart + +#endif // VM_NATIVE_ENTRY_TEST_H_ diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc new file mode 100644 index 00000000000..86cd37a500c --- /dev/null +++ b/runtime/vm/object.cc @@ -0,0 +1,6314 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/object.h" + +#include "vm/assembler.h" +#include "vm/assert.h" +#include "vm/bigint_operations.h" +#include "vm/bootstrap.h" +#include "vm/code_generator.h" +#include "vm/code_index_table.h" +#include "vm/code_patcher.h" +#include "vm/compiler.h" +#include "vm/compiler_stats.h" +#include "vm/class_finalizer.h" +#include "vm/dart.h" +#include "vm/debuginfo.h" +#include "vm/growable_array.h" +#include "vm/heap.h" +#include "vm/ic_stubs.h" +#include "vm/object_store.h" +#include "vm/parser.h" +#include "vm/runtime_entry.h" +#include "vm/scopes.h" +#include "vm/timer.h" + +namespace dart { + +DEFINE_FLAG(bool, expose_core_impl, false, + "Enables access to core implementation library (only for testing)."); +DEFINE_FLAG(bool, generate_gdb_symbols, false, + "Generate symbols of generated dart functions for debugging with GDB"); + +cpp_vtable Object::handle_vtable_ = 0; +cpp_vtable Smi::handle_vtable_ = 0; + +// These are initialized to a value that will force a illegal memory access if +// they are being used. +#if defined(RAW_NULL) +#error RAW_NULL should not be defined. +#endif +#define RAW_NULL kHeapObjectTag +RawObject* Object::null_ = reinterpret_cast(RAW_NULL); +RawInstance* Object::sentinel_ = reinterpret_cast(RAW_NULL); +RawInstance* Object::transition_sentinel_ = + reinterpret_cast(RAW_NULL); +RawClass* Object::class_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::null_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::type_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::parameterized_type_class_ = + reinterpret_cast(RAW_NULL); +RawClass* Object::type_parameter_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::instantiated_type_class_ = + reinterpret_cast(RAW_NULL); +RawClass* Object::type_arguments_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::type_array_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::instantiated_type_arguments_class_ = + reinterpret_cast(RAW_NULL); +RawClass* Object::function_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::field_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::token_stream_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::script_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::library_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::library_prefix_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::code_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::instructions_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::pc_descriptors_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::exception_handlers_class_ = + reinterpret_cast(RAW_NULL); +RawClass* Object::context_class_ = reinterpret_cast(RAW_NULL); +RawClass* Object::context_scope_class_ = reinterpret_cast(RAW_NULL); +#undef RAW_NULL + +int Object::GetSingletonClassIndex(const RawClass* raw_class) { + ASSERT(raw_class->IsHeapObject()); + if (raw_class == class_class()) { + return kClassClass; + } else if (raw_class == null_class()) { + return kNullClass; + } else if (raw_class == type_class()) { + return kTypeClass; + } else if (raw_class == parameterized_type_class()) { + return kParameterizedTypeClass; + } else if (raw_class == type_parameter_class()) { + return kTypeParameterClass; + } else if (raw_class == instantiated_type_class()) { + return kInstantiatedTypeClass; + } else if (raw_class == type_arguments_class()) { + return kTypeArgumentsClass; + } else if (raw_class == type_array_class()) { + return kTypeArrayClass; + } else if (raw_class == instantiated_type_arguments_class()) { + return kInstantiatedTypeArgumentsClass; + } else if (raw_class == function_class()) { + return kFunctionClass; + } else if (raw_class == field_class()) { + return kFieldClass; + } else if (raw_class == token_stream_class()) { + return kTokenStreamClass; + } else if (raw_class == script_class()) { + return kScriptClass; + } else if (raw_class == library_class()) { + return kLibraryClass; + } else if (raw_class == library_prefix_class()) { + return kLibraryPrefixClass; + } else if (raw_class == code_class()) { + return kCodeClass; + } else if (raw_class == instructions_class()) { + return kInstructionsClass; + } else if (raw_class == pc_descriptors_class()) { + return kPcDescriptorsClass; + } else if (raw_class == exception_handlers_class()) { + return kExceptionHandlersClass; + } else if (raw_class == context_class()) { + return kContextClass; + } else if (raw_class == context_scope_class()) { + return kContextScopeClass; + } + return kInvalidIndex; +} + + +RawClass* Object::GetSingletonClass(int index) { + switch (index) { + case kClassClass: return class_class(); + case kNullClass: return null_class(); + case kTypeClass: return type_class(); + case kParameterizedTypeClass: return parameterized_type_class(); + case kTypeParameterClass: return type_parameter_class(); + case kInstantiatedTypeClass: return instantiated_type_class(); + case kTypeArgumentsClass: return type_arguments_class(); + case kTypeArrayClass: return type_array_class(); + case kInstantiatedTypeArgumentsClass: + return instantiated_type_arguments_class(); + case kFunctionClass: return function_class(); + case kFieldClass: return field_class(); + case kTokenStreamClass: return token_stream_class(); + case kScriptClass: return script_class(); + case kLibraryClass: return library_class(); + case kLibraryPrefixClass: return library_prefix_class(); + case kCodeClass: return code_class(); + case kInstructionsClass: return instructions_class(); + case kPcDescriptorsClass: return pc_descriptors_class(); + case kExceptionHandlersClass: return exception_handlers_class(); + case kContextClass: return context_class(); + case kContextScopeClass: return context_scope_class(); + default: break; + } + UNREACHABLE(); + return reinterpret_cast(kHeapObjectTag); // return RAW_NULL. +} + + +const char* Object::GetSingletonClassName(int index) { + switch (index) { + case kClassClass: return "Class"; + case kNullClass: return "Null"; + case kTypeClass: return "Type"; + case kParameterizedTypeClass: return "ParameterizedType"; + case kTypeParameterClass: return "TypeParameter"; + case kInstantiatedTypeClass: return "InstantiatedType"; + case kTypeArgumentsClass: return "TypeArguments"; + case kTypeArrayClass: return "TypeArray"; + case kInstantiatedTypeArgumentsClass: return "InstantiatedTypeArguments"; + case kFunctionClass: return "Function"; + case kFieldClass: return "Field"; + case kTokenStreamClass: return "TokenStream"; + case kScriptClass: return "Script"; + case kLibraryClass: return "Library"; + case kLibraryPrefixClass: return "LibraryPrefix"; + case kCodeClass: return "Code"; + case kInstructionsClass: return "Instructions"; + case kPcDescriptorsClass: return "PcDescriptors"; + case kExceptionHandlersClass: return "ExceptionHandlers"; + case kContextClass: return "Context"; + case kContextScopeClass: return "ContextScope"; + default: break; + } + UNREACHABLE(); + return NULL; +} + + +void Object::InitOnce() { + // TODO(iposva): NoGCScope needs to be added here. + ASSERT(class_class() == null_); + // Initialize the static vtable values. + { + Object fake_object; + Smi fake_smi; + Object::handle_vtable_ = fake_object.vtable(); + Smi::handle_vtable_ = fake_smi.vtable(); + } + + Heap* heap = Isolate::Current()->heap(); + // Allocate and initialize the null instance, except its class_ field. + // 'null_' must be the first object allocated as it is used in allocation to + // clear the object. + { + uword address = heap->Allocate(Instance::InstanceSize(), Heap::kOld); + null_ = reinterpret_cast(address + kHeapObjectTag); + InitializeObject(address, Instance::InstanceSize()); // Using 'null_'. + } + + // Initialize object_store empty array to null_ in order to be able to check + // if the empty array was allocated (RAW_NULL is not available). + Isolate::Current()->object_store()->set_empty_array(Array::Handle()); + + Class& cls = Class::Handle(); + + // Allocate and initialize the class class. + // At this point, class_class_ is still RAW_NULL, i.e. different from 'null_', + // since 'null_' is not RAW_NULL anymore. However, class_class_ == null_ must + // be true before calling Class::New(), or it will fail. + class_class_ = Class::Handle().raw(); // Set 'class_class_' to 'null_'. + cls = Class::New(); + cls.set_is_finalized(); + class_class_ = cls.raw(); + // Make the class_ field point to itself. + class_class_->ptr()->class_ = class_class_; + + // Allocate and initialize the null class. + cls = Class::New(); + null_class_ = cls.raw(); + + // Complete initialization of null_ instance, i.e. initialize its class_ + // field. + null_->ptr()->class_ = null_class_; + + // Allocate and initialize the sentinel values of an instance class. + { + cls = Class::New(); + Instance& sentinel = Instance::Handle(); + sentinel ^= Object::Allocate(cls, Instance::InstanceSize(), Heap::kOld); + sentinel_ = sentinel.raw(); + + Instance& transition_sentinel = Instance::Handle(); + transition_sentinel ^= + Object::Allocate(cls, Instance::InstanceSize(), Heap::kOld); + transition_sentinel_ = transition_sentinel.raw(); + } + + // Allocate the remaining VM internal classes. + cls = Class::New(); + type_class_ = cls.raw(); + + cls = Class::New(); + parameterized_type_class_ = cls.raw(); + + cls = Class::New(); + type_parameter_class_ = cls.raw(); + + cls = Class::New(); + instantiated_type_class_ = cls.raw(); + + cls = Class::New(); + type_arguments_class_ = cls.raw(); + + cls = Class::New(); + type_array_class_ = cls.raw(); + + cls = Class::New(); + instantiated_type_arguments_class_ = cls.raw(); + + cls = Class::New(); + function_class_ = cls.raw(); + + cls = Class::New(); + field_class_ = cls.raw(); + + cls = Class::New(); + token_stream_class_ = cls.raw(); + + cls = Class::New