mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:03:19 +00:00
Initial checkin.
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@15 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
6e10bccb64
commit
4c0f559d23
46
runtime/PRESUBMIT.py
Normal file
46
runtime/PRESUBMIT.py
Normal file
|
@ -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)
|
411
runtime/bin/bin.gypi
Normal file
411
runtime/bin/bin.gypi
Normal file
|
@ -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' ],
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
29
runtime/bin/builtin.cc
Normal file
29
runtime/bin/builtin.cc
Normal file
|
@ -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 <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
13
runtime/bin/builtin.dart
Normal file
13
runtime/bin/builtin.dart
Normal file
|
@ -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";
|
||||||
|
}
|
42
runtime/bin/builtin.h
Normal file
42
runtime/bin/builtin.h
Normal file
|
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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_
|
156
runtime/bin/builtin_in.cc
Normal file
156
runtime/bin/builtin_in.cc
Normal file
|
@ -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 <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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<Dart_NativeFunction>(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));
|
||||||
|
}
|
57
runtime/bin/dartutils.cc
Normal file
57
runtime/bin/dartutils.cc
Normal file
|
@ -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));
|
||||||
|
}
|
36
runtime/bin/dartutils.h
Normal file
36
runtime/bin/dartutils.h
Normal file
|
@ -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_
|
65
runtime/bin/directory.cc
Normal file
65
runtime/bin/directory.cc
Normal file
|
@ -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();
|
||||||
|
}
|
57
runtime/bin/directory.dart
Normal file
57
runtime/bin/directory.dart
Normal file
|
@ -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));
|
||||||
|
}
|
26
runtime/bin/directory.h
Normal file
26
runtime/bin/directory.h
Normal file
|
@ -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_
|
130
runtime/bin/directory_impl.dart
Normal file
130
runtime/bin/directory_impl.dart
Normal file
|
@ -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;
|
||||||
|
}
|
72
runtime/bin/directory_linux.cc
Normal file
72
runtime/bin/directory_linux.cc
Normal file
|
@ -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 <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#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<intptr_t>(dir_pointer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Directory::Close(intptr_t dir) {
|
||||||
|
DIR* dir_pointer = reinterpret_cast<DIR*>(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*>(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);
|
||||||
|
}
|
||||||
|
}
|
72
runtime/bin/directory_macos.cc
Normal file
72
runtime/bin/directory_macos.cc
Normal file
|
@ -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 <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#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<intptr_t>(dir_pointer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Directory::Close(intptr_t dir) {
|
||||||
|
DIR* dir_pointer = reinterpret_cast<DIR*>(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*>(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);
|
||||||
|
}
|
||||||
|
}
|
24
runtime/bin/directory_win.cc
Normal file
24
runtime/bin/directory_win.cc
Normal file
|
@ -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();
|
||||||
|
}
|
68
runtime/bin/eventhandler.cc
Normal file
68
runtime/bin/eventhandler.cc
Normal file
|
@ -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<EventHandler*>(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<intptr_t>(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();
|
||||||
|
}
|
28
runtime/bin/eventhandler.dart
Normal file
28
runtime/bin/eventhandler.dart
Normal file
|
@ -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;
|
||||||
|
}
|
50
runtime/bin/eventhandler.h
Normal file
50
runtime/bin/eventhandler.h
Normal file
|
@ -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_
|
327
runtime/bin/eventhandler_linux.cc
Normal file
327
runtime/bin/eventhandler_linux.cc
Normal file
|
@ -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 <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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<int64_t>(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<PortData*>(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<PortData*>(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<struct pollfd*>(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<EventHandlerImplementation*>(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);
|
||||||
|
}
|
57
runtime/bin/eventhandler_linux.h
Normal file
57
runtime/bin/eventhandler_linux.h
Normal file
|
@ -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_
|
325
runtime/bin/eventhandler_macos.cc
Normal file
325
runtime/bin/eventhandler_macos.cc
Normal file
|
@ -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 <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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<int64_t>(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<PortData*>(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<PortData*>(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<struct pollfd*>(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<EventHandlerImplementation*>(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);
|
||||||
|
}
|
57
runtime/bin/eventhandler_macos.h
Normal file
57
runtime/bin/eventhandler_macos.h
Normal file
|
@ -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_
|
778
runtime/bin/eventhandler_win.cc
Normal file
778
runtime/bin/eventhandler_win.cc
Normal file
|
@ -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 <process.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <mswsock.h>
|
||||||
|
|
||||||
|
#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>(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>(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<ULONG_PTR>(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<SOCKET>(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<Handle*>(msg->id);
|
||||||
|
ASSERT(socket_desc != NULL);
|
||||||
|
if (socket_desc->is_listen_socket()) {
|
||||||
|
ListenSocket* listen_socket =
|
||||||
|
reinterpret_cast<ListenSocket*>(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<ClientSocket*>(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<ListenSocket*>(key);
|
||||||
|
HandleAccept(listen_socket, buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IOBuffer::kRead: {
|
||||||
|
ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(key);
|
||||||
|
HandleRead(client_socket, bytes, buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IOBuffer::kWrite: {
|
||||||
|
ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(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<OVERLAPPED*>(msg));
|
||||||
|
if (!ok) {
|
||||||
|
FATAL("PostQueuedCompletionStatus failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned int __stdcall EventHandlerThread(void* args) {
|
||||||
|
EventHandlerImplementation* handler =
|
||||||
|
reinterpret_cast<EventHandlerImplementation*>(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<InterruptMessage*>(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");
|
||||||
|
}
|
||||||
|
}
|
333
runtime/bin/eventhandler_win.h
Normal file
333
runtime/bin/eventhandler_win.h
Normal file
|
@ -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 <winsock2.h>
|
||||||
|
#include <mswsock.h>
|
||||||
|
|
||||||
|
#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<char*>(&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>(handle)) { type_ = kFile; }
|
||||||
|
FileHandle(HANDLE handle, Dart_Port port)
|
||||||
|
: Handle(reinterpret_cast<HANDLE>(handle), port) { type_ = kFile; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class SocketHandle : public Handle {
|
||||||
|
public:
|
||||||
|
SOCKET socket() { return reinterpret_cast<SOCKET>(handle_); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit SocketHandle(SOCKET s) : Handle(reinterpret_cast<HANDLE>(s)) {}
|
||||||
|
SocketHandle(SOCKET s, Dart_Port port)
|
||||||
|
: Handle(reinterpret_cast<HANDLE>(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_
|
41
runtime/bin/fdutils.h
Normal file
41
runtime/bin/fdutils.h
Normal file
|
@ -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_
|
104
runtime/bin/fdutils_linux.cc
Normal file
104
runtime/bin/fdutils_linux.cc
Normal file
|
@ -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 <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#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<char*>(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<char*>(reinterpret_cast<const char*>(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;
|
||||||
|
}
|
104
runtime/bin/fdutils_macos.cc
Normal file
104
runtime/bin/fdutils_macos.cc
Normal file
|
@ -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 <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#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<char*>(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<char*>(reinterpret_cast<const char*>(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;
|
||||||
|
}
|
243
runtime/bin/file.cc
Normal file
243
runtime/bin/file.cc
Normal file
|
@ -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<char*>(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<const char*>(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<File*>(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<intptr_t>(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<void*>(&buffer), 1);
|
||||||
|
if (bytes_read >= 0) {
|
||||||
|
return_value = static_cast<intptr_t>(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<uint8_t>(value & 0xff);
|
||||||
|
int bytes_written = file->Write(reinterpret_cast<void*>(&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<const void*>(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<void*>(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<void*>(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();
|
||||||
|
}
|
65
runtime/bin/file.dart
Normal file
65
runtime/bin/file.dart
Normal file
|
@ -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<int> from the file.
|
||||||
|
// TODO(jrgfogh): Remove this call.
|
||||||
|
int readList(List<int> buffer, int offset, int bytes);
|
||||||
|
|
||||||
|
// Synchronously write a List<int> to the file.
|
||||||
|
// TODO(jrgfogh): Remove this call.
|
||||||
|
int writeList(List<int> 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";
|
||||||
|
}
|
78
runtime/bin/file.h
Normal file
78
runtime/bin/file.h
Normal file
|
@ -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 <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
// 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_
|
223
runtime/bin/file_impl.dart
Normal file
223
runtime/bin/file_impl.dart
Normal file
|
@ -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<int> 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<int> pattern, void callback(List<int> buffer)) {
|
||||||
|
List<int> buffer = new List<int>(_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<int> resultBuffer = new List<int>(resultBufferSize);
|
||||||
|
resultBuffer.copyFrom(buffer, 0, 0, resultBufferSize);
|
||||||
|
callback(resultBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(srdjan: move this method to Lists.dart (helper method).
|
||||||
|
static int indexOf(List<int> buffer, List<int> 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<int> 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<int> 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<int> buffer, int offset, int bytes)
|
||||||
|
native "File_ReadList";
|
||||||
|
|
||||||
|
int writeList(List<int> 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<int> 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;
|
||||||
|
}
|
128
runtime/bin/file_linux.cc
Normal file
128
runtime/bin/file_linux.cc
Normal file
|
@ -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 <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <libgen.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() > 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 "/";
|
||||||
|
}
|
128
runtime/bin/file_macos.cc
Normal file
128
runtime/bin/file_macos.cc
Normal file
|
@ -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 <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <libgen.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() > 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 "/";
|
||||||
|
}
|
60
runtime/bin/file_test.cc
Normal file
60
runtime/bin/file_test.cc
Normal file
|
@ -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;
|
||||||
|
}
|
130
runtime/bin/file_win.cc
Normal file
130
runtime/bin/file_win.cc
Normal file
|
@ -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 <fcntl.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#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 "\\\\";
|
||||||
|
}
|
186
runtime/bin/gen_snapshot.cc
Normal file
186
runtime/bin/gen_snapshot.cc
Normal file
|
@ -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 <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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<const char*>(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 [<vm-flags>] "
|
||||||
|
"[<dart-script-file>]\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;
|
||||||
|
}
|
126
runtime/bin/globals.h
Normal file
126
runtime/bin/globals.h
Normal file
|
@ -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 <windows.h>
|
||||||
|
#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 <typename T>
|
||||||
|
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_
|
25
runtime/bin/input_stream.dart
Normal file
25
runtime/bin/input_stream.dart
Normal file
|
@ -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<int> 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<int> pattern, void callback(List<int> buffer));
|
||||||
|
}
|
||||||
|
|
257
runtime/bin/main.cc
Normal file
257
runtime/bin/main.cc
Normal file
|
@ -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 <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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<const char*>(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 [<vm-flags>] <dart-script-file> [<dart-options>]\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;
|
||||||
|
}
|
19
runtime/bin/output_stream.dart
Normal file
19
runtime/bin/output_stream.dart
Normal file
|
@ -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<int> buffer, int offset, int len, void callback());
|
||||||
|
}
|
||||||
|
|
70
runtime/bin/process.cc
Normal file
70
runtime/bin/process.cc
Normal file
|
@ -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<char *>(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();
|
||||||
|
}
|
66
runtime/bin/process.dart
Normal file
66
runtime/bin/process.dart
Normal file
|
@ -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<String> 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;
|
||||||
|
}
|
31
runtime/bin/process.h
Normal file
31
runtime/bin/process.h
Normal file
|
@ -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_
|
153
runtime/bin/process_impl.dart
Normal file
153
runtime/bin/process_impl.dart
Normal file
|
@ -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<String> arguments) {
|
||||||
|
_path = path;
|
||||||
|
{
|
||||||
|
int len = arguments.length;
|
||||||
|
_arguments = new ObjectArray<String>(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<String> 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<int> buffer = new List<int>(4);
|
||||||
|
SocketInputStream input = _exitHandler.inputStream;
|
||||||
|
|
||||||
|
int getExitValue(List<int> 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<String> _arguments;
|
||||||
|
|
||||||
|
Socket _in;
|
||||||
|
|
||||||
|
Socket _out;
|
||||||
|
|
||||||
|
Socket _err;
|
||||||
|
|
||||||
|
Socket _exitHandler;
|
||||||
|
|
||||||
|
int _pid;
|
||||||
|
|
||||||
|
bool _closed;
|
||||||
|
|
||||||
|
bool _killed;
|
||||||
|
|
||||||
|
bool _started;
|
||||||
|
|
||||||
|
var _exitHandlerCallback;
|
||||||
|
}
|
302
runtime/bin/process_linux.cc
Normal file
302
runtime/bin/process_linux.cc
Normal file
|
@ -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 <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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<ActiveProcess> 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<char *>(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<char* const*>(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;
|
||||||
|
}
|
302
runtime/bin/process_macos.cc
Normal file
302
runtime/bin/process_macos.cc
Normal file
|
@ -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 <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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<ActiveProcess> 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<char *>(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<char* const*>(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;
|
||||||
|
}
|
158
runtime/bin/process_script.cc
Normal file
158
runtime/bin/process_script.cc
Normal file
|
@ -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 <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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<char*>(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<char*>(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<char*>(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<char*>(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);
|
||||||
|
}
|
60
runtime/bin/process_script.h
Normal file
60
runtime/bin/process_script.h
Normal file
|
@ -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 <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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<char **>(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_
|
57
runtime/bin/process_test.cc
Normal file
57
runtime/bin/process_test.cc
Normal file
|
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run ./process_test <outstream> <echocount> <exitcode> <crash>
|
||||||
|
* <outstream>: 0 = stdout, 1 = stderr, 2 = stdout and stderr
|
||||||
|
* <echocount>: program terminates after <echocount> replies
|
||||||
|
* <exitcode>: program terminates with exit code <exitcode>
|
||||||
|
* <crash>: 0 = program terminates regularly, 1 = program segfaults
|
||||||
|
*/
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc != 5) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"./process_test <outstream> <echocount> <exitcode> <crash>\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;
|
||||||
|
}
|
89
runtime/bin/process_win.cc
Normal file
89
runtime/bin/process_win.cc
Normal file
|
@ -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<intptr_t>(process_info.hProcess);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Process::Kill(intptr_t id) {
|
||||||
|
HANDLE process_handle = reinterpret_cast<HANDLE>(id);
|
||||||
|
BOOL result = TerminateProcess(process_handle, -1);
|
||||||
|
if (result == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CloseHandle(process_handle);
|
||||||
|
return true;
|
||||||
|
}
|
98
runtime/bin/run_vm_tests.cc
Normal file
98
runtime/bin/run_vm_tests.cc
Normal file
|
@ -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 <stdio.h>
|
||||||
|
|
||||||
|
#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 | <test name>]\n");
|
||||||
|
fprintf(stderr, "run_vm_tests <test name> [vm-flags ...]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void* TestIsolateInitCallback(void* data) {
|
||||||
|
ASSERT(data == NULL);
|
||||||
|
return reinterpret_cast<void*>(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);
|
||||||
|
}
|
128
runtime/bin/set.h
Normal file
128
runtime/bin/set.h
Normal file
|
@ -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 <stdlib.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set implements a collection of distinct objects.
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
class Set {
|
||||||
|
private:
|
||||||
|
struct Node {
|
||||||
|
T object_;
|
||||||
|
Node* next_;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
class Iterator {
|
||||||
|
public:
|
||||||
|
explicit Iterator(Set<T>* 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<T>* 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_
|
||||||
|
|
125
runtime/bin/set_test.cc
Normal file
125
runtime/bin/set_test.cc
Normal file
|
@ -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<int> 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<int> set;
|
||||||
|
int i;
|
||||||
|
for (i = 1; i <= 10; i++) {
|
||||||
|
set.Add(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<int>::Iterator iterator(&set);
|
||||||
|
int value;
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
while (iterator.HasNext()) {
|
||||||
|
iterator.GetNext(&value);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
EXPECT(i == 10);
|
||||||
|
EXPECT(!set.IsEmpty());
|
||||||
|
|
||||||
|
Set<int> emptyset;
|
||||||
|
Set<int>::Iterator emptyiterator(&emptyset);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (emptyiterator.HasNext()) {
|
||||||
|
emptyiterator.GetNext(&value);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
EXPECT(i == 0);
|
||||||
|
EXPECT(emptyset.IsEmpty());
|
||||||
|
}
|
||||||
|
|
16
runtime/bin/snapshot_empty.cc
Normal file
16
runtime/bin/snapshot_empty.cc
Normal file
|
@ -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 <inttypes.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
const uint8_t* snapshot_buffer = NULL;
|
22
runtime/bin/snapshot_in.cc
Normal file
22
runtime/bin/snapshot_in.cc
Normal file
|
@ -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 <inttypes.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// 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_;
|
143
runtime/bin/socket.cc
Normal file
143
runtime/bin/socket.cc
Normal file
|
@ -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<void*>(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();
|
||||||
|
}
|
116
runtime/bin/socket.dart
Normal file
116
runtime/bin/socket.dart
Normal file
|
@ -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<int> 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<int> 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();
|
||||||
|
}
|
37
runtime/bin/socket.h
Normal file
37
runtime/bin/socket.h
Normal file
|
@ -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_
|
287
runtime/bin/socket_impl.dart
Normal file
287
runtime/bin/socket_impl.dart
Normal file
|
@ -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<int> 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<int> 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<int> buffer, int offset, int bytes)
|
||||||
|
native "Socket_ReadList";
|
||||||
|
|
||||||
|
int writeList(List<int> 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<int> 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;
|
||||||
|
}
|
||||||
|
|
138
runtime/bin/socket_linux.cc
Normal file
138
runtime/bin/socket_linux.cc
Normal file
|
@ -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 <arpa/inet.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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<struct sockaddr *>(&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<struct sockaddr *>(&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<struct sockaddr *>(&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;
|
||||||
|
}
|
138
runtime/bin/socket_macos.cc
Normal file
138
runtime/bin/socket_macos.cc
Normal file
|
@ -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 <arpa/inet.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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<struct sockaddr *>(&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<struct sockaddr *>(&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<struct sockaddr *>(&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;
|
||||||
|
}
|
166
runtime/bin/socket_stream.dart
Normal file
166
runtime/bin/socket_stream.dart
Normal file
|
@ -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<int> 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<int> newBuffer = new List<int>(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<int> buffer, List<int> 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<int> _getBufferedData(List<int> newBuffer, int appendingBufferSpace) {
|
||||||
|
List<int> buffer;
|
||||||
|
int newDataStart = 0;
|
||||||
|
if (_buffer !== null) {
|
||||||
|
buffer = new List<int>(_buffer.length + appendingBufferSpace);
|
||||||
|
buffer.copyFrom(_buffer, 0, 0, _buffer.length);
|
||||||
|
newDataStart = _buffer.length;
|
||||||
|
_buffer = null;
|
||||||
|
} else {
|
||||||
|
buffer = new List<int>(appendingBufferSpace);
|
||||||
|
}
|
||||||
|
buffer.copyFrom(newBuffer, 0, newDataStart, appendingBufferSpace);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void readUntil(List<int> pattern, void callback(List<int> buffer)) {
|
||||||
|
void doRead() {
|
||||||
|
int size = _socket.available();
|
||||||
|
List<int> buffer = new List<int>(size);
|
||||||
|
int result = _socket.readList(buffer, 0, size);
|
||||||
|
if (result > 0) {
|
||||||
|
// TODO(hpayer): Avoid copying of data before pattern matching.
|
||||||
|
List<int> 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<int> finalBuffer = new List<int>(finalBufferSize);
|
||||||
|
finalBuffer.copyFrom(newBuffer, 0, 0, finalBufferSize);
|
||||||
|
if (finalBufferSize < newBuffer.length) {
|
||||||
|
List<int> remainder =
|
||||||
|
new List<int>(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<int> _buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SocketOutputStream implements OutputStream {
|
||||||
|
SocketOutputStream(Socket socket) {
|
||||||
|
_socket = socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool write(List<int> 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;
|
||||||
|
}
|
147
runtime/bin/socket_win.cc
Normal file
147
runtime/bin/socket_win.cc
Normal file
|
@ -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 <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <mswsock.h>
|
||||||
|
|
||||||
|
#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<ClientSocket*>(fd);
|
||||||
|
return client_socket->Available();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
intptr_t Socket::Read(intptr_t fd, void* buffer, intptr_t num_bytes) {
|
||||||
|
ASSERT(reinterpret_cast<Handle*>(fd)->is_client_socket());
|
||||||
|
ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(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<Handle*>(fd)->is_client_socket());
|
||||||
|
ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(fd);
|
||||||
|
return client_socket->Write(buffer, num_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
intptr_t Socket::GetPort(intptr_t fd) {
|
||||||
|
ASSERT(reinterpret_cast<Handle*>(fd)->is_socket());
|
||||||
|
SocketHandle* socket_handle = reinterpret_cast<SocketHandle*>(fd);
|
||||||
|
struct sockaddr_in socket_address;
|
||||||
|
socklen_t size = sizeof(socket_address);
|
||||||
|
if (getsockname(socket_handle->socket(),
|
||||||
|
reinterpret_cast<struct sockaddr *>(&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<struct sockaddr *>(&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<intptr_t>(client_socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
intptr_t ServerSocket::Accept(intptr_t fd) {
|
||||||
|
ListenSocket* listen_socket = reinterpret_cast<ListenSocket*>(fd);
|
||||||
|
ClientSocket* client_socket = listen_socket->Accept();
|
||||||
|
if (client_socket != NULL) {
|
||||||
|
return reinterpret_cast<intptr_t>(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<const char*>(&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<struct sockaddr *>(&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<intptr_t>(listen_socket);
|
||||||
|
}
|
19
runtime/bin/timer.dart
Normal file
19
runtime/bin/timer.dart
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
|
162
runtime/bin/timer_impl.dart
Normal file
162
runtime/bin/timer_impl.dart
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
4
runtime/codereview.settings
Normal file
4
runtime/codereview.settings
Normal file
|
@ -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:
|
33
runtime/dart-runtime.gyp
Normal file
33
runtime/dart-runtime.gyp
Normal file
|
@ -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',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
395
runtime/include/dart_api.h
Executable file
395
runtime/include/dart_api.h
Executable file
|
@ -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 <inttypes.h>
|
||||||
|
#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 <assert.h>
|
||||||
|
|
||||||
|
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_
|
142
runtime/lib/array.cc
Normal file
142
runtime/lib/array.cc
Normal file
|
@ -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<const Object*> 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<const Object*> 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<const Object*> 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<const Object*> 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<const Object*> 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<const Object*> args;
|
||||||
|
Exceptions::ThrowByType(Exceptions::kIllegalArgument, args);
|
||||||
|
}
|
||||||
|
intptr_t icount = count.Value();
|
||||||
|
if (icount < 0) {
|
||||||
|
GrowableArray<const Object*> 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<const Object*> arguments;
|
||||||
|
arguments.Add(&src_start);
|
||||||
|
Exceptions::ThrowByType(Exceptions::kIndexOutOfRange, arguments);
|
||||||
|
}
|
||||||
|
if ((idst_start + icount) > dest.Length()) {
|
||||||
|
GrowableArray<const Object*> 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
|
294
runtime/lib/array.dart
Normal file
294
runtime/lib/array.dart
Normal file
|
@ -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<T> {
|
||||||
|
factory Array.from(Iterable<T> other) {
|
||||||
|
GrowableObjectArray<T> array = new GrowableObjectArray<T>();
|
||||||
|
for (final e in other) {
|
||||||
|
array.add(e);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Array.fromArray(Array<T> other, int startIndex, int endIndex) {
|
||||||
|
Array array = new Array<T>();
|
||||||
|
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<T>();
|
||||||
|
} else {
|
||||||
|
return new ObjectArray<T>(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListFactory<T> {
|
||||||
|
|
||||||
|
factory List.from(Iterable<T> other) {
|
||||||
|
GrowableObjectArray<T> list = new GrowableObjectArray<T>();
|
||||||
|
for (final e in other) {
|
||||||
|
list.add(e);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory List.fromList(List<T> other, int startIndex, int endIndex) {
|
||||||
|
List list = new List<T>();
|
||||||
|
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<T>();
|
||||||
|
} else {
|
||||||
|
return new ObjectArray<T>(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(srdjan): Use shared array implementation.
|
||||||
|
class ObjectArray<T> implements Array<T> {
|
||||||
|
|
||||||
|
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<T> filter(bool f(T element)) {
|
||||||
|
return Collections.filter(this, new GrowableObjectArray<T>(), 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<T> iterator() {
|
||||||
|
return new FixedSizeArrayIterator<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(T element) {
|
||||||
|
throw const UnsupportedOperationException(
|
||||||
|
"Cannot add to a non-extendable array");
|
||||||
|
}
|
||||||
|
|
||||||
|
void addLast(T element) {
|
||||||
|
add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addAll(Collection<T> 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<T> implements Array<T> {
|
||||||
|
|
||||||
|
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<T> filter(bool f(T element)) {
|
||||||
|
return Collections.filter(this, new GrowableObjectArray<T>(), 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<T> iterator() {
|
||||||
|
return new FixedSizeArrayIterator<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(T element) {
|
||||||
|
throw const UnsupportedOperationException(
|
||||||
|
"Cannot add to an immutable array");
|
||||||
|
}
|
||||||
|
|
||||||
|
void addLast(T element) {
|
||||||
|
add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addAll(Collection<T> 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<T> implements Iterator<T> {
|
||||||
|
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<T> _array;
|
||||||
|
final int _length; // Cache array length for faster access.
|
||||||
|
int _pos;
|
||||||
|
}
|
90
runtime/lib/arrays.dart
Normal file
90
runtime/lib/arrays.dart
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
6
runtime/lib/bool.dart
Normal file
6
runtime/lib/bool.dart
Normal file
|
@ -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 {
|
||||||
|
}
|
26
runtime/lib/clock.cc
Normal file
26
runtime/lib/clock.cc
Normal file
|
@ -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
|
21
runtime/lib/clock.dart
Normal file
21
runtime/lib/clock.dart
Normal file
|
@ -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";
|
||||||
|
|
||||||
|
}
|
43
runtime/lib/collections.dart
Normal file
43
runtime/lib/collections.dart
Normal file
|
@ -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<Object> iterable, void f(Object o)) {
|
||||||
|
for (final e in iterable) {
|
||||||
|
f(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool some(Iterable<Object> iterable, bool f(Object o)) {
|
||||||
|
for (final e in iterable) {
|
||||||
|
if (f(e)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool every(Iterable<Object> iterable, bool f(Object o)) {
|
||||||
|
for (final e in iterable) {
|
||||||
|
if (!f(e)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<Object> filter(Iterable<Object> source,
|
||||||
|
List<Object> destination,
|
||||||
|
bool f(Object o)) {
|
||||||
|
for (final e in source) {
|
||||||
|
if (f(e)) destination.add(e);
|
||||||
|
}
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isEmpty(Iterable<Object> iterable) {
|
||||||
|
return !iterable.iterator().hasNext();
|
||||||
|
}
|
||||||
|
}
|
155
runtime/lib/date_time.cc
Normal file
155
runtime/lib/date_time.cc
Normal file
|
@ -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<time_t>(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
|
424
runtime/lib/date_time.dart
Normal file
424
runtime/lib/date_time.dart
Normal file
|
@ -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";
|
||||||
|
}
|
212
runtime/lib/double.cc
Normal file
212
runtime/lib/double.cc
Normal file
|
@ -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 <math.h>
|
||||||
|
|
||||||
|
#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<const Object*> 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<intptr_t>(result))));
|
||||||
|
} else if ((Mint::kMinValue <= result) && (result <= Mint::kMaxValue)) {
|
||||||
|
arguments->SetReturn(Mint::Handle(Mint::New(static_cast<int64_t>(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
|
||||||
|
|
200
runtime/lib/double.dart
Normal file
200
runtime/lib/double.dart
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
288
runtime/lib/error.cc
Normal file
288
runtime/lib/error.cc
Normal file
|
@ -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<int>(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
|
55
runtime/lib/error.dart
Normal file
55
runtime/lib/error.dart
Normal file
|
@ -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;
|
||||||
|
}
|
19
runtime/lib/error.h
Normal file
19
runtime/lib/error.h
Normal file
|
@ -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_
|
||||||
|
|
182
runtime/lib/growable_array.dart
Normal file
182
runtime/lib/growable_array.dart
Normal file
|
@ -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<T> implements Array<T> {
|
||||||
|
Array<T> backingArray;
|
||||||
|
|
||||||
|
void copyFrom(Array<Object> 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<T>(4) {}
|
||||||
|
|
||||||
|
GrowableObjectArray.withCapacity(int capacity) {
|
||||||
|
_length = 0;
|
||||||
|
if (capacity <= 0) {
|
||||||
|
capacity = 4;
|
||||||
|
}
|
||||||
|
backingArray = new ObjectArray<T>(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrowableObjectArray._usingArray(Array<T> array) {
|
||||||
|
backingArray = array;
|
||||||
|
_length = array.length;
|
||||||
|
if (_length == 0) {
|
||||||
|
grow(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
factory GrowableObjectArray.from(Collection<T> 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<T> newArray = new ObjectArray<T>(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<T> 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<T> filter(bool f(T element)) {
|
||||||
|
return Collections.filter(this, new GrowableObjectArray<T>(), 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<T> iterator() {
|
||||||
|
return new VariableSizeArrayIterator<T>(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Iterator for arrays with variable size.
|
||||||
|
class VariableSizeArrayIterator<T> implements Iterator<T> {
|
||||||
|
VariableSizeArrayIterator(GrowableObjectArray<T> array)
|
||||||
|
: _array = array, _pos = 0 {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasNext() {
|
||||||
|
return _array._length > _pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
T next() {
|
||||||
|
if (!hasNext()) {
|
||||||
|
throw const NoMoreElementsException();
|
||||||
|
}
|
||||||
|
return _array[_pos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
final GrowableObjectArray<T> _array;
|
||||||
|
int _pos;
|
||||||
|
}
|
||||||
|
|
108
runtime/lib/immutable_map.dart
Normal file
108
runtime/lib/immutable_map.dart
Normal file
|
@ -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<K, V> implements Map<K, V> {
|
||||||
|
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<K> getKeys() {
|
||||||
|
int numKeys = length;
|
||||||
|
Array<K> array = new Array<K>(numKeys);
|
||||||
|
for (int i = 0; i < numKeys; i++) {
|
||||||
|
array[i] = kvPairs_[i*2];
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<V> getValues() {
|
||||||
|
int numValues = length;
|
||||||
|
Array<K> array = new Array<K>(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;
|
||||||
|
}
|
||||||
|
}
|
586
runtime/lib/integers.cc
Normal file
586
runtime/lib/integers.cc
Normal file
|
@ -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<int64_t>(left.Value()) *
|
||||||
|
static_cast<int64_t>(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<uint64_t>((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
|
182
runtime/lib/integers.dart
Normal file
182
runtime/lib/integers.dart
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
378
runtime/lib/isolate.cc
Normal file
378
runtime/lib/isolate.cc
Normal file
|
@ -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<void*>(ptr), new_size);
|
||||||
|
return reinterpret_cast<uint8_t*>(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<const Object*> 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<const Object*> 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<const Object*> 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<const Object*> 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<IsolateStartData*>(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<uword>(&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<const Object*> 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<const Object*> 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<uword>(
|
||||||
|
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<char*>(preserved_zone->Allocate(err_len));
|
||||||
|
OS::SNPrint(
|
||||||
|
const_cast<char*>(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
|
166
runtime/lib/isolate.dart
Normal file
166
runtime/lib/isolate.dart
Normal file
|
@ -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<SendPort> spawn(Isolate isolate, bool isLight) {
|
||||||
|
Promise<SendPort> result = new Promise<SendPort>();
|
||||||
|
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; }
|
||||||
|
}
|
34
runtime/lib/lib_impl_sources.gypi
Normal file
34
runtime/lib/lib_impl_sources.gypi
Normal file
|
@ -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',
|
||||||
|
],
|
||||||
|
}
|
17
runtime/lib/lib_sources.gypi
Normal file
17
runtime/lib/lib_sources.gypi
Normal file
|
@ -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',
|
||||||
|
],
|
||||||
|
}
|
183
runtime/lib/math.cc
Normal file
183
runtime/lib/math.cc
Normal file
|
@ -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 <ctype.h> // 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<double>(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<const Object*> 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<const Object*> args;
|
||||||
|
args.Add(&value);
|
||||||
|
Exceptions::ThrowByType(Exceptions::kBadNumberFormat, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dart
|
78
runtime/lib/math.dart
Normal file
78
runtime/lib/math.dart
Normal file
|
@ -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";
|
||||||
|
}
|
35
runtime/lib/object.cc
Normal file
35
runtime/lib/object.cc
Normal file
|
@ -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<const Object*> args;
|
||||||
|
Exceptions::ThrowByType(Exceptions::kNullPointer, args);
|
||||||
|
}
|
||||||
|
GrowableArray<const Object*> 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
|
17
runtime/lib/object.dart
Normal file
17
runtime/lib/object.dart
Normal file
|
@ -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; }
|
||||||
|
}
|
65
runtime/lib/regexp.cc
Normal file
65
runtime/lib/regexp.cc
Normal file
|
@ -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<const Object*> 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
|
101
runtime/lib/regexp.dart
Normal file
101
runtime/lib/regexp.dart
Normal file
|
@ -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<String> groups(List<int> groups) {
|
||||||
|
var groupsList = new List<String>(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<int> _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<Match> allMatches(String str) {
|
||||||
|
var jsregexMatches = new GrowableObjectArray<JSRegExpMatch>();
|
||||||
|
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";
|
||||||
|
}
|
187
runtime/lib/regexp_jsc.cc
Normal file
187
runtime/lib/regexp_jsc.cc
Normal file
|
@ -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<uint16_t*>(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<intptr_t>(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<const Object*> 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<jscre::JSRegExp*>(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<int*>(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
|
23
runtime/lib/regexp_jsc.h
Normal file
23
runtime/lib/regexp_jsc.h
Normal file
|
@ -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_
|
150
runtime/lib/string.cc
Normal file
150
runtime/lib/string.cc
Normal file
|
@ -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<uint32_t*>(
|
||||||
|
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<const Object*> args;
|
||||||
|
args.Add(&index_object);
|
||||||
|
Exceptions::ThrowByType(Exceptions::kIllegalArgument, args);
|
||||||
|
}
|
||||||
|
element ^= index_object.raw();
|
||||||
|
intptr_t value = element.Value();
|
||||||
|
if (value < 0) {
|
||||||
|
GrowableArray<const Object*> 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<const Object*> 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<const Object*> 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<const Object*> 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<const Object*> 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
|
483
runtime/lib/string.dart
Normal file
483
runtime/lib/string.dart
Normal file
|
@ -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<int> 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<int> 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<int> charCodes = new Array<int>(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<String> stringArray = new Array<String>(numValues);
|
||||||
|
int resultLength = 0;
|
||||||
|
for (int i = 0; i < numValues; i++) {
|
||||||
|
String str = values[i].toString();
|
||||||
|
resultLength += str.length;
|
||||||
|
stringArray[i] = str;
|
||||||
|
}
|
||||||
|
Array<int> codepoints = new Array<int>(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<Match> allMatches(String str) {
|
||||||
|
GrowableObjectArray<Match> result = new GrowableObjectArray<Match>();
|
||||||
|
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<String> split(Pattern pattern) {
|
||||||
|
if (pattern is RegExp) {
|
||||||
|
throw "Unimplemented split with RegExp";
|
||||||
|
}
|
||||||
|
GrowableObjectArray<String> result = new GrowableObjectArray<String>();
|
||||||
|
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<String> splitChars() {
|
||||||
|
int len = this.length;
|
||||||
|
final result = new Array<String>(len);
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
result[i] = this[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<int> charCodes() {
|
||||||
|
int len = this.length;
|
||||||
|
final result = new Array<int>(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<int> charCodes = new Array<int>(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<String> 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<String> 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<String> 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<String> groups(Array<int> groups) {
|
||||||
|
Array<String> result = new Array<String>();
|
||||||
|
for (int g in groups) {
|
||||||
|
result.add(group(g));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int _start;
|
||||||
|
final String str;
|
||||||
|
final String pattern;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue