Version 0.5.20.0
svn merge -r 24061:24101 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
git-svn-id: http://dart.googlecode.com/svn/trunk@24106 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/runtime/bin/bin.gypi b/runtime/bin/bin.gypi
index 03c75c6..22db244 100644
--- a/runtime/bin/bin.gypi
+++ b/runtime/bin/bin.gypi
@@ -230,8 +230,12 @@
'sources': [
'../include/dart_api.h',
'../include/dart_debugger_api.h',
+ '../include/dart_mirrors_api.h',
+ '../include/dart_native_api.h',
'../vm/dart_api_impl.cc',
'../vm/debugger_api_impl.cc',
+ '../vm/mirrors_api_impl.cc',
+ '../vm/native_api_impl.cc',
'<(version_cc_file)',
],
'defines': [
diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc
index 375c6d5..8134848 100644
--- a/runtime/bin/dartutils.cc
+++ b/runtime/bin/dartutils.cc
@@ -5,6 +5,7 @@
#include "bin/dartutils.h"
#include "include/dart_api.h"
+#include "include/dart_native_api.h"
#include "platform/assert.h"
#include "platform/globals.h"
diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h
index 48db952..026b83a 100644
--- a/runtime/bin/dartutils.h
+++ b/runtime/bin/dartutils.h
@@ -6,6 +6,7 @@
#define BIN_DARTUTILS_H_
#include "include/dart_api.h"
+#include "include/dart_native_api.h"
#include "platform/assert.h"
#include "platform/globals.h"
diff --git a/runtime/bin/directory.cc b/runtime/bin/directory.cc
index 6ca4ac0..7ce1787 100644
--- a/runtime/bin/directory.cc
+++ b/runtime/bin/directory.cc
@@ -150,11 +150,11 @@
Dart_Null(),
0,
NULL);
- SyncDirectoryListing sync_listing(results);
- Directory::List(DartUtils::GetStringValue(path),
- DartUtils::GetBooleanValue(recursive),
- DartUtils::GetBooleanValue(follow_links),
- &sync_listing);
+ SyncDirectoryListing sync_listing(results,
+ DartUtils::GetStringValue(path),
+ DartUtils::GetBooleanValue(recursive),
+ DartUtils::GetBooleanValue(follow_links));
+ Directory::List(&sync_listing);
Dart_SetReturnValue(args, results);
Dart_ExitScope();
}
@@ -220,41 +220,75 @@
return CObject::IllegalArgumentError();
}
+static CObject* CreateIllegalArgumentError() {
+ // Respond with an illegal argument list error message.
+ CObjectArray* error = new CObjectArray(CObject::NewArray(3));
+ error->SetAt(0, new CObjectInt32(
+ CObject::NewInt32(AsyncDirectoryListing::kListError)));
+ error->SetAt(1, CObject::Null());
+ error->SetAt(2, CObject::IllegalArgumentError());
+ return error;
+}
-static CObject* DirectoryListRequest(const CObjectArray& request,
- Dart_Port response_port) {
+static CObject* DirectoryListStartRequest(const CObjectArray& request) {
if (request.Length() == 4 &&
request[1]->IsString() &&
request[2]->IsBool() &&
request[3]->IsBool()) {
- AsyncDirectoryListing* dir_listing =
- new AsyncDirectoryListing(response_port);
CObjectString path(request[1]);
CObjectBool recursive(request[2]);
CObjectBool follow_links(request[3]);
- bool completed = Directory::List(
- path.CString(), recursive.Value(), follow_links.Value(), dir_listing);
- delete dir_listing;
- CObjectArray* response = new CObjectArray(CObject::NewArray(2));
- response->SetAt(
- 0,
- new CObjectInt32(CObject::NewInt32(AsyncDirectoryListing::kListDone)));
- response->SetAt(1, CObject::Bool(completed));
+ AsyncDirectoryListing* dir_listing =
+ new AsyncDirectoryListing(path.CString(),
+ recursive.Value(),
+ follow_links.Value());
+ if (dir_listing->error()) {
+ // Report error now, so we capture the correct OSError.
+ CObject* err = CObject::NewOSError();
+ delete dir_listing;
+ CObjectArray* error = new CObjectArray(CObject::NewArray(3));
+ error->SetAt(0, new CObjectInt32(
+ CObject::NewInt32(AsyncDirectoryListing::kListError)));
+ error->SetAt(1, request[1]);
+ error->SetAt(2, err);
+ return error;
+ }
+ // TODO(ajohnsen): Consider returning the first few results.
+ return new CObjectIntptr(CObject::NewIntptr(
+ reinterpret_cast<intptr_t>(dir_listing)));
+ }
+ return CreateIllegalArgumentError();
+}
+
+
+static CObject* DirectoryListNextRequest(const CObjectArray& request) {
+ if (request.Length() == 2 &&
+ request[1]->IsIntptr()) {
+ CObjectIntptr ptr(request[1]);
+ AsyncDirectoryListing* dir_listing =
+ reinterpret_cast<AsyncDirectoryListing*>(ptr.Value());
+ const int kArraySize = 128;
+ CObjectArray* response = new CObjectArray(CObject::NewArray(kArraySize));
+ dir_listing->SetArray(response, kArraySize);
+ Directory::List(dir_listing);
+ // In case the listing ended before it hit the buffer length, we need to
+ // override the array length.
+ response->AsApiCObject()->value.as_array.length = dir_listing->index();
return response;
}
- // Respond with an illegal argument list error message.
- CObjectArray* response = new CObjectArray(CObject::NewArray(3));
- response->SetAt(0, new CObjectInt32(
- CObject::NewInt32(AsyncDirectoryListing::kListError)));
- response->SetAt(1, CObject::Null());
- response->SetAt(2, CObject::IllegalArgumentError());
- Dart_PostCObject(response_port, response->AsApiCObject());
+ return CreateIllegalArgumentError();
+}
- response = new CObjectArray(CObject::NewArray(2));
- response->SetAt(
- 0, new CObjectInt32(CObject::NewInt32(AsyncDirectoryListing::kListDone)));
- response->SetAt(1, CObject::False());
- return response;
+
+static CObject* DirectoryListStopRequest(const CObjectArray& request) {
+ if (request.Length() == 2 && request[1]->IsIntptr()) {
+ CObjectIntptr ptr(request[1]);
+ AsyncDirectoryListing* dir_listing =
+ reinterpret_cast<AsyncDirectoryListing*>(ptr.Value());
+ delete dir_listing;
+ return new CObjectBool(CObject::Bool(true));
+ }
+ return CreateIllegalArgumentError();
}
@@ -294,8 +328,14 @@
case Directory::kCreateTempRequest:
response = DirectoryCreateTempRequest(request);
break;
- case Directory::kListRequest:
- response = DirectoryListRequest(request, reply_port_id);
+ case Directory::kListStartRequest:
+ response = DirectoryListStartRequest(request);
+ break;
+ case Directory::kListNextRequest:
+ response = DirectoryListNextRequest(request);
+ break;
+ case Directory::kListStopRequest:
+ response = DirectoryListStopRequest(request);
break;
case Directory::kRenameRequest:
response = DirectoryRenameRequest(request, reply_port_id);
@@ -328,39 +368,46 @@
}
-CObjectArray* AsyncDirectoryListing::NewResponse(Response type, char* arg) {
- CObjectArray* response = new CObjectArray(CObject::NewArray(2));
- response->SetAt(0, new CObjectInt32(CObject::NewInt32(type)));
- response->SetAt(1, new CObjectString(CObject::NewString(arg)));
- return response;
+bool AsyncDirectoryListing::AddFileSystemEntityToResponse(Response type,
+ char* arg) {
+ array_->SetAt(index_++, new CObjectInt32(CObject::NewInt32(type)));
+ if (arg != NULL) {
+ array_->SetAt(index_++, new CObjectString(CObject::NewString(arg)));
+ } else {
+ array_->SetAt(index_++, CObject::Null());
+ }
+ return index_ < length_;
}
bool AsyncDirectoryListing::HandleDirectory(char* dir_name) {
- CObjectArray* response = NewResponse(kListDirectory, dir_name);
- return Dart_PostCObject(response_port_, response->AsApiCObject());
+ return AddFileSystemEntityToResponse(kListDirectory, dir_name);
}
bool AsyncDirectoryListing::HandleFile(char* file_name) {
- CObjectArray* response = NewResponse(kListFile, file_name);
- return Dart_PostCObject(response_port_, response->AsApiCObject());
+ return AddFileSystemEntityToResponse(kListFile, file_name);
}
bool AsyncDirectoryListing::HandleLink(char* link_name) {
- CObjectArray* response = NewResponse(kListLink, link_name);
- return Dart_PostCObject(response_port_, response->AsApiCObject());
+ return AddFileSystemEntityToResponse(kListLink, link_name);
+}
+
+void AsyncDirectoryListing::HandleDone() {
+ AddFileSystemEntityToResponse(kListDone, NULL);
}
bool AsyncDirectoryListing::HandleError(const char* dir_name) {
CObject* err = CObject::NewOSError();
+ array_->SetAt(index_++, new CObjectInt32(CObject::NewInt32(kListError)));
CObjectArray* response = new CObjectArray(CObject::NewArray(3));
response->SetAt(0, new CObjectInt32(CObject::NewInt32(kListError)));
response->SetAt(1, new CObjectString(CObject::NewString(dir_name)));
response->SetAt(2, err);
- return Dart_PostCObject(response_port_, response->AsApiCObject());
+ array_->SetAt(index_++, response);
+ return index_ < length_;
}
bool SyncDirectoryListing::HandleDirectory(char* dir_name) {
@@ -401,5 +448,47 @@
return true;
}
+
+static bool ListNext(DirectoryListing* listing) {
+ switch (listing->top()->Next(listing)) {
+ case kListFile:
+ return listing->HandleFile(listing->CurrentPath());
+
+ case kListLink:
+ return listing->HandleLink(listing->CurrentPath());
+
+ case kListDirectory:
+ if (listing->recursive()) {
+ listing->Push(new DirectoryListingEntry(listing->top()));
+ }
+ return listing->HandleDirectory(listing->CurrentPath());
+
+ case kListError:
+ return listing->HandleError(listing->CurrentPath());
+
+ case kListDone:
+ listing->Pop();
+ if (listing->IsEmpty()) {
+ listing->HandleDone();
+ return false;
+ } else {
+ return true;
+ }
+
+ default:
+ UNREACHABLE();
+ }
+ return false;
+}
+
+void Directory::List(DirectoryListing* listing) {
+ if (listing->error()) {
+ listing->HandleError("Invalid path");
+ listing->HandleDone();
+ } else {
+ while (ListNext(listing)) {}
+ }
+}
+
} // namespace bin
} // namespace dart
diff --git a/runtime/bin/directory.h b/runtime/bin/directory.h
index a19d274..5fa10f0 100644
--- a/runtime/bin/directory.h
+++ b/runtime/bin/directory.h
@@ -15,13 +15,165 @@
namespace dart {
namespace bin {
+enum ListType {
+ kListFile = 0,
+ kListDirectory = 1,
+ kListLink = 2,
+ kListError = 3,
+ kListDone = 4
+};
+
+class PathBuffer {
+ public:
+ PathBuffer();
+ ~PathBuffer() {
+ free(data_);
+ }
+
+ bool Add(const char* name);
+ bool AddW(const wchar_t* name);
+
+ char* AsString() const;
+ wchar_t* AsStringW() const;
+
+ void Reset(int new_length);
+
+ int length() const {
+ return length_;
+ }
+
+ private:
+ void* data_;
+ int length_;
+
+ DISALLOW_COPY_AND_ASSIGN(PathBuffer);
+};
+
+class DirectoryListing;
+
+struct LinkList;
+
+// DirectoryListingEntry is used as a stack item, when performing recursive
+// directory listing. By using DirectoryListingEntry as stack elements, a
+// directory listing can be paused e.g. when a buffer is full, and resumed
+// later on.
+//
+// The stack is managed by the DirectoryListing's PathBuffer. Each
+// DirectoryListingEntry stored a entry-length, that it'll reset the PathBuffer
+// to on each call to Next.
+class DirectoryListingEntry {
+ public:
+ explicit DirectoryListingEntry(DirectoryListingEntry* parent)
+ : parent_(parent), lister_(0), done_(false), link_(NULL) {}
+
+ ~DirectoryListingEntry() {
+ ResetLink();
+ }
+
+ ListType Next(DirectoryListing* listing);
+
+ DirectoryListingEntry* parent() const {
+ return parent_;
+ }
+
+ LinkList* link() {
+ return link_;
+ }
+
+ void set_link(LinkList* link) {
+ link_ = link;
+ }
+
+ void ResetLink() {
+ if (link_ != NULL && (parent_ == NULL || parent_->link_ != link_)) {
+ free(link_);
+ link_ = NULL;
+ }
+ if (parent_ != NULL) {
+ link_ = parent_->link_;
+ }
+ }
+
+ private:
+ DirectoryListingEntry* parent_;
+ intptr_t lister_;
+ bool done_;
+ int path_length_;
+ LinkList* link_;
+
+ DISALLOW_COPY_AND_ASSIGN(DirectoryListingEntry);
+};
+
class DirectoryListing {
public:
- virtual ~DirectoryListing() {}
+ DirectoryListing(const char* dir_name, bool recursive, bool follow_links)
+ : top_(NULL),
+ error_(false),
+ recursive_(recursive),
+ follow_links_(follow_links) {
+ if (!path_buffer_.Add(dir_name)) {
+ error_ = true;
+ }
+ Push(new DirectoryListingEntry(NULL));
+ }
+
+ virtual ~DirectoryListing() {
+ while (!IsEmpty()) {
+ Pop();
+ }
+ }
+
virtual bool HandleDirectory(char* dir_name) = 0;
virtual bool HandleFile(char* file_name) = 0;
- virtual bool HandleLink(char* file_name) = 0;
+ virtual bool HandleLink(char* link_name) = 0;
virtual bool HandleError(const char* dir_name) = 0;
+ virtual void HandleDone() {}
+
+ void Push(DirectoryListingEntry* directory) {
+ top_ = directory;
+ }
+
+ void Pop() {
+ ASSERT(!IsEmpty());
+ DirectoryListingEntry* current = top_;
+ top_ = top_->parent();
+ delete current;
+ }
+
+ bool IsEmpty() const {
+ return top_ == NULL;
+ }
+
+ DirectoryListingEntry* top() const {
+ return top_;
+ }
+
+ bool recursive() const {
+ return recursive_;
+ }
+
+ bool follow_links() const {
+ return follow_links_;
+ }
+
+ char* CurrentPath() {
+ return path_buffer_.AsString();
+ }
+
+ PathBuffer& path_buffer() {
+ return path_buffer_;
+ }
+
+ bool error() const {
+ return error_;
+ }
+
+ private:
+ PathBuffer path_buffer_;
+ DirectoryListingEntry* top_;
+ bool error_;
+ bool recursive_;
+ bool follow_links_;
};
@@ -35,17 +187,34 @@
kListDone = 4
};
- explicit AsyncDirectoryListing(Dart_Port response_port)
- : response_port_(response_port) {}
+ AsyncDirectoryListing(const char* dir_name,
+ bool recursive,
+ bool follow_links)
+ : DirectoryListing(dir_name, recursive, follow_links) {}
+
virtual ~AsyncDirectoryListing() {}
virtual bool HandleDirectory(char* dir_name);
virtual bool HandleFile(char* file_name);
virtual bool HandleLink(char* file_name);
virtual bool HandleError(const char* dir_name);
+ virtual void HandleDone();
+
+ void SetArray(CObjectArray* array, intptr_t length) {
+ ASSERT(length % 2 == 0);
+ array_ = array;
+ index_ = 0;
+ length_ = length;
+ }
+
+ intptr_t index() const {
+ return index_;
+ }
private:
- CObjectArray* NewResponse(Response response, char* arg);
- Dart_Port response_port_;
+ bool AddFileSystemEntityToResponse(Response response, char* arg);
+ CObjectArray* array_;
+ intptr_t index_;
+ intptr_t length_;
DISALLOW_IMPLICIT_CONSTRUCTORS(AsyncDirectoryListing);
};
@@ -53,8 +222,12 @@
class SyncDirectoryListing: public DirectoryListing {
public:
- explicit SyncDirectoryListing(Dart_Handle results)
- : results_(results) {
+ SyncDirectoryListing(Dart_Handle results,
+ const char* dir_name,
+ bool recursive,
+ bool follow_links)
+ : DirectoryListing(dir_name, recursive, follow_links),
+ results_(results) {
add_string_ = DartUtils::NewString("add");
directory_class_ =
DartUtils::GetDartClass(DartUtils::kIOLibURL, "Directory");
@@ -95,14 +268,13 @@
kDeleteRequest = 1,
kExistsRequest = 2,
kCreateTempRequest = 3,
- kListRequest = 4,
- kRenameRequest = 5
+ kListStartRequest = 4,
+ kListNextRequest = 5,
+ kListStopRequest = 6,
+ kRenameRequest = 7
};
- static bool List(const char* path,
- bool recursive,
- bool follow_links,
- DirectoryListing* listing);
+ static void List(DirectoryListing* listing);
static ExistsResult Exists(const char* path);
static char* Current();
static bool SetCurrent(const char* path);
diff --git a/runtime/bin/directory_android.cc b/runtime/bin/directory_android.cc
index dc5bad2..5c0f891 100644
--- a/runtime/bin/directory_android.cc
+++ b/runtime/bin/directory_android.cc
@@ -21,41 +21,47 @@
namespace dart {
namespace bin {
-class PathBuffer {
- public:
- PathBuffer() : length(0) {
- data = new char[PATH_MAX + 1];
- }
- ~PathBuffer() {
- delete[] data;
- }
+PathBuffer::PathBuffer() : length_(0) {
+ data_ = new char[PATH_MAX + 1];
+}
- char* data;
- int length;
+bool PathBuffer::AddW(const wchar_t* name) {
+ UNREACHABLE();
+ return false;
+}
- bool Add(const char* name) {
- int written = snprintf(data + length,
- PATH_MAX - length,
- "%s",
- name);
- data[PATH_MAX] = '\0';
- if (written <= PATH_MAX - length &&
- written >= 0 &&
- static_cast<size_t>(written) == strnlen(name, PATH_MAX + 1)) {
- length += written;
- return true;
- } else {
- errno = ENAMETOOLONG;
- return false;
- }
- }
+char* PathBuffer::AsString() const {
+ return reinterpret_cast<char*>(data_);
+}
- void Reset(int new_length) {
- length = new_length;
- data[length] = '\0';
+wchar_t* PathBuffer::AsStringW() const {
+ UNREACHABLE();
+ return NULL;
+}
+
+bool PathBuffer::Add(const char* name) {
+ char* data = AsString();
+ int written = snprintf(data + length_,
+ PATH_MAX - length_,
+ "%s",
+ name);
+ data[PATH_MAX] = '\0';
+ if (written <= PATH_MAX - length_ &&
+ written >= 0 &&
+ static_cast<size_t>(written) == strnlen(name, PATH_MAX + 1)) {
+ length_ += written;
+ return true;
+ } else {
+ errno = ENAMETOOLONG;
+ return false;
}
-};
+}
+
+void PathBuffer::Reset(int new_length) {
+ length_ = new_length;
+ AsString()[length_] = '\0';
+}
// A linked list of symbolic links, with their unique file system identifiers.
@@ -67,110 +73,54 @@
};
-// Forward declarations.
-static bool ListRecursively(PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing* listing);
-static bool DeleteRecursively(PathBuffer* path);
-
-
-static void PostError(DirectoryListing *listing,
- const char* dir_name) {
- listing->HandleError(dir_name);
-}
-
-
-static bool HandleDir(char* dir_name,
- PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing *listing) {
- if (strcmp(dir_name, ".") == 0) return true;
- if (strcmp(dir_name, "..") == 0) return true;
- if (!path->Add(dir_name)) {
- PostError(listing, path->data);
- return false;
+ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
+ if (done_) {
+ return kListDone;
}
- return listing->HandleDirectory(path->data) &&
- (!recursive ||
- ListRecursively(path, recursive, follow_links, seen, listing));
-}
+ if (lister_ == 0) {
+ if (!listing->path_buffer().Add(File::PathSeparator())) {
+ done_ = true;
+ return kListError;
+ }
+ path_length_ = listing->path_buffer().length();
+ do {
+ lister_ = reinterpret_cast<intptr_t>(
+ opendir(listing->path_buffer().AsString()));
+ } while (lister_ == 0 && errno == EINTR);
-static bool HandleFile(char* file_name,
- PathBuffer* path,
- DirectoryListing *listing) {
- if (!path->Add(file_name)) {
- PostError(listing, path->data);
- return false;
+ if (lister_ == 0) {
+ done_ = true;
+ return kListError;
+ }
}
- return listing->HandleFile(path->data);
-}
-
-
-static bool HandleLink(char* link_name,
- PathBuffer* path,
- DirectoryListing *listing) {
- if (!path->Add(link_name)) {
- PostError(listing, path->data);
- return false;
- }
- return listing->HandleLink(path->data);
-}
-
-
-static bool ListRecursively(PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing *listing) {
- if (!path->Add(File::PathSeparator())) {
- PostError(listing, path->data);
- return false;
- }
- DIR* dir_pointer;
- do {
- dir_pointer = opendir(path->data);
- } while (dir_pointer == NULL && errno == EINTR);
- if (dir_pointer == NULL) {
- PostError(listing, path->data);
- return false;
- }
+ // Reset.
+ listing->path_buffer().Reset(path_length_);
+ ResetLink();
// Iterate the directory and post the directories and files to the
// ports.
- int path_length = path->length;
int status = 0;
- bool success = true;
dirent entry;
dirent* result;
- while ((status = TEMP_FAILURE_RETRY(readdir_r(dir_pointer,
- &entry,
- &result))) == 0 &&
- result != NULL) {
+ if ((status = TEMP_FAILURE_RETRY(readdir_r(reinterpret_cast<DIR*>(lister_),
+ &entry,
+ &result))) == 0 &&
+ result != NULL) {
+ if (!listing->path_buffer().Add(entry.d_name)) {
+ done_ = true;
+ return kListError;
+ }
switch (entry.d_type) {
case DT_DIR:
- success = HandleDir(entry.d_name,
- path,
- recursive,
- follow_links,
- seen,
- listing) && success;
- break;
+ if (strcmp(entry.d_name, ".") == 0) return Next(listing);
+ if (strcmp(entry.d_name, "..") == 0) return Next(listing);
+ return kListDirectory;
case DT_REG:
- success = HandleFile(entry.d_name,
- path,
- listing) && success;
- break;
+ return kListFile;
case DT_LNK:
- if (!follow_links) {
- success = HandleLink(entry.d_name,
- path,
- listing) && success;
- break;
+ if (!listing->follow_links()) {
+ return kListLink;
}
// Else fall through to next case.
// Fall through.
@@ -180,103 +130,77 @@
// the actual entry type. Notice that stat returns the type of
// the file pointed to.
struct stat entry_info;
- if (!path->Add(entry.d_name)) {
- success = false;
- break;
- }
int stat_success;
- stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
+ stat_success = TEMP_FAILURE_RETRY(
+ lstat(listing->path_buffer().AsString(), &entry_info));
if (stat_success == -1) {
- success = false;
- PostError(listing, path->data);
- break;
+ return kListError;
}
- if (follow_links && S_ISLNK(entry_info.st_mode)) {
+ if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) {
// Check to see if we are in a loop created by a symbolic link.
LinkList current_link = { entry_info.st_dev,
entry_info.st_ino,
- seen };
- LinkList* previous = seen;
- bool looping_link = false;
+ link_ };
+ LinkList* previous = link_;
while (previous != NULL) {
if (previous->dev == current_link.dev &&
previous->ino == current_link.ino) {
// Report the looping link as a link, rather than following it.
- path->Reset(path_length);
- success = HandleLink(entry.d_name,
- path,
- listing) && success;
- looping_link = true;
- break;
+ return kListLink;
}
previous = previous->next;
}
- if (looping_link) break;
- stat_success = TEMP_FAILURE_RETRY(stat(path->data, &entry_info));
+ stat_success = TEMP_FAILURE_RETRY(
+ stat(listing->path_buffer().AsString(), &entry_info));
if (stat_success == -1) {
// Report a broken link as a link, even if follow_links is true.
- path->Reset(path_length);
- success = HandleLink(entry.d_name,
- path,
- listing) && success;
- break;
+ return kListLink;
}
if (S_ISDIR(entry_info.st_mode)) {
// Recurse into the subdirectory with current_link added to the
// linked list of seen file system links.
- path->Reset(path_length);
- success = HandleDir(entry.d_name,
- path,
- recursive,
- follow_links,
- ¤t_link,
- listing) && success;
- break;
+ link_ = new LinkList(current_link);
+ if (strcmp(entry.d_name, ".") == 0) return Next(listing);
+ if (strcmp(entry.d_name, "..") == 0) return Next(listing);
+ return kListDirectory;
}
}
- path->Reset(path_length);
if (S_ISDIR(entry_info.st_mode)) {
- success = HandleDir(entry.d_name,
- path,
- recursive,
- follow_links,
- seen,
- listing) && success;
+ if (strcmp(entry.d_name, ".") == 0) return Next(listing);
+ if (strcmp(entry.d_name, "..") == 0) return Next(listing);
+ return kListDirectory;
} else if (S_ISREG(entry_info.st_mode)) {
- success = HandleFile(entry.d_name,
- path,
- listing) && success;
+ return kListFile;
} else if (S_ISLNK(entry_info.st_mode)) {
- success = HandleLink(entry.d_name,
- path,
- listing) && success;
+ return kListLink;
}
- break;
}
+
default:
break;
}
- path->Reset(path_length);
}
+ done_ = true;
if (status != 0) {
errno = status;
- success = false;
- PostError(listing, path->data);
+ return kListError;
}
- if (closedir(dir_pointer) == -1) {
- success = false;
- PostError(listing, path->data);
+ if (closedir(reinterpret_cast<DIR*>(lister_)) == -1) {
+ return kListError;
}
- return success;
+ return kListDone;
}
+static bool DeleteRecursively(PathBuffer* path);
+
+
static bool DeleteFile(char* file_name,
PathBuffer* path) {
- return path->Add(file_name) && unlink(path->data) == 0;
+ return path->Add(file_name) && unlink(path->AsString()) == 0;
}
@@ -292,10 +216,10 @@
// Do not recurse into links for deletion. Instead delete the link.
// If it's a file, delete it.
struct stat st;
- if (TEMP_FAILURE_RETRY(lstat(path->data, &st)) == -1) {
+ if (TEMP_FAILURE_RETRY(lstat(path->AsString(), &st)) == -1) {
return false;
} else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
- return (unlink(path->data) == 0);
+ return (unlink(path->AsString()) == 0);
}
if (!path->Add(File::PathSeparator())) return false;
@@ -304,7 +228,7 @@
// directory.
DIR* dir_pointer;
do {
- dir_pointer = opendir(path->data);
+ dir_pointer = opendir(path->AsString());
} while (dir_pointer == NULL && errno == EINTR);
if (dir_pointer == NULL) {
@@ -312,7 +236,7 @@
}
// Iterate the directory and delete all files and directories.
- int path_length = path->length;
+ int path_length = path->length();
int read = 0;
bool success = true;
dirent entry;
@@ -342,7 +266,8 @@
success = false;
break;
}
- int lstat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
+ int lstat_success = TEMP_FAILURE_RETRY(
+ lstat(path->AsString(), &entry_info));
if (lstat_success == -1) {
success = false;
break;
@@ -366,26 +291,13 @@
if ((read != 0) ||
(closedir(dir_pointer) == -1) ||
- (remove(path->data) == -1)) {
+ (remove(path->AsString()) == -1)) {
return false;
}
return success;
}
-bool Directory::List(const char* dir_name,
- bool recursive,
- bool follow_links,
- DirectoryListing *listing) {
- PathBuffer path;
- if (!path.Add(dir_name)) {
- PostError(listing, dir_name);
- return false;
- }
- return ListRecursively(&path, recursive, follow_links, NULL, listing);
-}
-
-
Directory::ExistsResult Directory::Exists(const char* dir_name) {
struct stat entry_info;
int success = TEMP_FAILURE_RETRY(stat(dir_name, &entry_info));
@@ -466,7 +378,7 @@
// The return value must be freed by the caller.
PathBuffer path;
path.Add(const_template);
- if (path.length == 0) {
+ if (path.length() == 0) {
// Android does not have a /tmp directory. A partial substitute,
// suitable for bring-up work and tests, is to create a tmp
// directory in /data/local/tmp.
@@ -479,7 +391,7 @@
mkdir(ANDROID_TEMP_DIR, 0777);
}
path.Add(ANDROID_TEMP_DIR "/temp_dir1_");
- } else if ((path.data)[path.length - 1] == '/') {
+ } else if ((path.AsString())[path.length() - 1] == '/') {
path.Add("temp_dir_");
}
if (!path.Add("XXXXXX")) {
@@ -488,14 +400,14 @@
}
char* result;
do {
- result = MakeTempDirectory(path.data);
+ result = MakeTempDirectory(path.AsString());
} while (result == NULL && errno == EINTR);
if (result == NULL) {
return NULL;
}
- int length = strnlen(path.data, PATH_MAX);
+ int length = strnlen(path.AsString(), PATH_MAX);
result = static_cast<char*>(malloc(length + 1));
- strncpy(result, path.data, length);
+ strncpy(result, path.AsString(), length);
result[length] = '\0';
return result;
}
diff --git a/runtime/bin/directory_linux.cc b/runtime/bin/directory_linux.cc
index bd84753..f715604 100644
--- a/runtime/bin/directory_linux.cc
+++ b/runtime/bin/directory_linux.cc
@@ -21,41 +21,47 @@
namespace dart {
namespace bin {
-class PathBuffer {
- public:
- PathBuffer() : length(0) {
- data = new char[PATH_MAX + 1];
- }
- ~PathBuffer() {
- delete[] data;
- }
+PathBuffer::PathBuffer() : length_(0) {
+ data_ = new char[PATH_MAX + 1];
+}
- char* data;
- int length;
+bool PathBuffer::AddW(const wchar_t* name) {
+ UNREACHABLE();
+ return false;
+}
- bool Add(const char* name) {
- int written = snprintf(data + length,
- PATH_MAX - length,
- "%s",
- name);
- data[PATH_MAX] = '\0';
- if (written <= PATH_MAX - length &&
- written >= 0 &&
- static_cast<size_t>(written) == strnlen(name, PATH_MAX + 1)) {
- length += written;
- return true;
- } else {
- errno = ENAMETOOLONG;
- return false;
- }
- }
+char* PathBuffer::AsString() const {
+ return reinterpret_cast<char*>(data_);
+}
- void Reset(int new_length) {
- length = new_length;
- data[length] = '\0';
+wchar_t* PathBuffer::AsStringW() const {
+ UNREACHABLE();
+ return NULL;
+}
+
+bool PathBuffer::Add(const char* name) {
+ char* data = AsString();
+ int written = snprintf(data + length_,
+ PATH_MAX - length_,
+ "%s",
+ name);
+ data[PATH_MAX] = '\0';
+ if (written <= PATH_MAX - length_ &&
+ written >= 0 &&
+ static_cast<size_t>(written) == strnlen(name, PATH_MAX + 1)) {
+ length_ += written;
+ return true;
+ } else {
+ errno = ENAMETOOLONG;
+ return false;
}
-};
+}
+
+void PathBuffer::Reset(int new_length) {
+ length_ = new_length;
+ AsString()[length_] = '\0';
+}
// A linked list of symbolic links, with their unique file system identifiers.
@@ -67,110 +73,54 @@
};
-// Forward declarations.
-static bool ListRecursively(PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing* listing);
-static bool DeleteRecursively(PathBuffer* path);
-
-
-static void PostError(DirectoryListing *listing,
- const char* dir_name) {
- listing->HandleError(dir_name);
-}
-
-
-static bool HandleDir(char* dir_name,
- PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing *listing) {
- if (strcmp(dir_name, ".") == 0) return true;
- if (strcmp(dir_name, "..") == 0) return true;
- if (!path->Add(dir_name)) {
- PostError(listing, path->data);
- return false;
+ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
+ if (done_) {
+ return kListDone;
}
- return listing->HandleDirectory(path->data) &&
- (!recursive ||
- ListRecursively(path, recursive, follow_links, seen, listing));
-}
+ if (lister_ == 0) {
+ if (!listing->path_buffer().Add(File::PathSeparator())) {
+ done_ = true;
+ return kListError;
+ }
+ path_length_ = listing->path_buffer().length();
+ do {
+ lister_ = reinterpret_cast<intptr_t>(
+ opendir(listing->path_buffer().AsString()));
+ } while (lister_ == 0 && errno == EINTR);
-static bool HandleFile(char* file_name,
- PathBuffer* path,
- DirectoryListing *listing) {
- if (!path->Add(file_name)) {
- PostError(listing, path->data);
- return false;
+ if (lister_ == 0) {
+ done_ = true;
+ return kListError;
+ }
}
- return listing->HandleFile(path->data);
-}
-
-
-static bool HandleLink(char* link_name,
- PathBuffer* path,
- DirectoryListing *listing) {
- if (!path->Add(link_name)) {
- PostError(listing, path->data);
- return false;
- }
- return listing->HandleLink(path->data);
-}
-
-
-static bool ListRecursively(PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing *listing) {
- if (!path->Add(File::PathSeparator())) {
- PostError(listing, path->data);
- return false;
- }
- DIR* dir_pointer;
- do {
- dir_pointer = opendir(path->data);
- } while (dir_pointer == NULL && errno == EINTR);
- if (dir_pointer == NULL) {
- PostError(listing, path->data);
- return false;
- }
+ // Reset.
+ listing->path_buffer().Reset(path_length_);
+ ResetLink();
// Iterate the directory and post the directories and files to the
// ports.
- int path_length = path->length;
int status = 0;
- bool success = true;
dirent entry;
dirent* result;
- while ((status = TEMP_FAILURE_RETRY(readdir_r(dir_pointer,
- &entry,
- &result))) == 0 &&
- result != NULL) {
+ if ((status = TEMP_FAILURE_RETRY(readdir_r(reinterpret_cast<DIR*>(lister_),
+ &entry,
+ &result))) == 0 &&
+ result != NULL) {
+ if (!listing->path_buffer().Add(entry.d_name)) {
+ done_ = true;
+ return kListError;
+ }
switch (entry.d_type) {
case DT_DIR:
- success = HandleDir(entry.d_name,
- path,
- recursive,
- follow_links,
- seen,
- listing) && success;
- break;
+ if (strcmp(entry.d_name, ".") == 0) return Next(listing);
+ if (strcmp(entry.d_name, "..") == 0) return Next(listing);
+ return kListDirectory;
case DT_REG:
- success = HandleFile(entry.d_name,
- path,
- listing) && success;
- break;
+ return kListFile;
case DT_LNK:
- if (!follow_links) {
- success = HandleLink(entry.d_name,
- path,
- listing) && success;
- break;
+ if (!listing->follow_links()) {
+ return kListLink;
}
// Else fall through to next case.
// Fall through.
@@ -180,103 +130,77 @@
// the actual entry type. Notice that stat returns the type of
// the file pointed to.
struct stat entry_info;
- if (!path->Add(entry.d_name)) {
- success = false;
- break;
- }
int stat_success;
- stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
+ stat_success = TEMP_FAILURE_RETRY(
+ lstat(listing->path_buffer().AsString(), &entry_info));
if (stat_success == -1) {
- success = false;
- PostError(listing, path->data);
- break;
+ return kListError;
}
- if (follow_links && S_ISLNK(entry_info.st_mode)) {
+ if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) {
// Check to see if we are in a loop created by a symbolic link.
LinkList current_link = { entry_info.st_dev,
entry_info.st_ino,
- seen };
- LinkList* previous = seen;
- bool looping_link = false;
+ link_ };
+ LinkList* previous = link_;
while (previous != NULL) {
if (previous->dev == current_link.dev &&
previous->ino == current_link.ino) {
// Report the looping link as a link, rather than following it.
- path->Reset(path_length);
- success = HandleLink(entry.d_name,
- path,
- listing) && success;
- looping_link = true;
- break;
+ return kListLink;
}
previous = previous->next;
}
- if (looping_link) break;
- stat_success = TEMP_FAILURE_RETRY(stat(path->data, &entry_info));
+ stat_success = TEMP_FAILURE_RETRY(
+ stat(listing->path_buffer().AsString(), &entry_info));
if (stat_success == -1) {
// Report a broken link as a link, even if follow_links is true.
- path->Reset(path_length);
- success = HandleLink(entry.d_name,
- path,
- listing) && success;
- break;
+ return kListLink;
}
if (S_ISDIR(entry_info.st_mode)) {
// Recurse into the subdirectory with current_link added to the
// linked list of seen file system links.
- path->Reset(path_length);
- success = HandleDir(entry.d_name,
- path,
- recursive,
- follow_links,
- ¤t_link,
- listing) && success;
- break;
+ link_ = new LinkList(current_link);
+ if (strcmp(entry.d_name, ".") == 0) return Next(listing);
+ if (strcmp(entry.d_name, "..") == 0) return Next(listing);
+ return kListDirectory;
}
}
- path->Reset(path_length);
if (S_ISDIR(entry_info.st_mode)) {
- success = HandleDir(entry.d_name,
- path,
- recursive,
- follow_links,
- seen,
- listing) && success;
+ if (strcmp(entry.d_name, ".") == 0) return Next(listing);
+ if (strcmp(entry.d_name, "..") == 0) return Next(listing);
+ return kListDirectory;
} else if (S_ISREG(entry_info.st_mode)) {
- success = HandleFile(entry.d_name,
- path,
- listing) && success;
+ return kListFile;
} else if (S_ISLNK(entry_info.st_mode)) {
- success = HandleLink(entry.d_name,
- path,
- listing) && success;
+ return kListLink;
}
- break;
}
+
default:
break;
}
- path->Reset(path_length);
}
+ done_ = true;
if (status != 0) {
errno = status;
- success = false;
- PostError(listing, path->data);
+ return kListError;
}
- if (closedir(dir_pointer) == -1) {
- success = false;
- PostError(listing, path->data);
+ if (closedir(reinterpret_cast<DIR*>(lister_)) == -1) {
+ return kListError;
}
- return success;
+ return kListDone;
}
+static bool DeleteRecursively(PathBuffer* path);
+
+
static bool DeleteFile(char* file_name,
PathBuffer* path) {
- return path->Add(file_name) && unlink(path->data) == 0;
+ return path->Add(file_name) && unlink(path->AsString()) == 0;
}
@@ -292,10 +216,10 @@
// Do not recurse into links for deletion. Instead delete the link.
// If it's a file, delete it.
struct stat st;
- if (TEMP_FAILURE_RETRY(lstat(path->data, &st)) == -1) {
+ if (TEMP_FAILURE_RETRY(lstat(path->AsString(), &st)) == -1) {
return false;
} else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
- return (unlink(path->data) == 0);
+ return (unlink(path->AsString()) == 0);
}
if (!path->Add(File::PathSeparator())) return false;
@@ -304,7 +228,7 @@
// directory.
DIR* dir_pointer;
do {
- dir_pointer = opendir(path->data);
+ dir_pointer = opendir(path->AsString());
} while (dir_pointer == NULL && errno == EINTR);
if (dir_pointer == NULL) {
@@ -312,7 +236,7 @@
}
// Iterate the directory and delete all files and directories.
- int path_length = path->length;
+ int path_length = path->length();
int read = 0;
bool success = true;
dirent entry;
@@ -342,7 +266,8 @@
success = false;
break;
}
- int lstat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
+ int lstat_success = TEMP_FAILURE_RETRY(
+ lstat(path->AsString(), &entry_info));
if (lstat_success == -1) {
success = false;
break;
@@ -366,26 +291,13 @@
if ((read != 0) ||
(closedir(dir_pointer) == -1) ||
- (remove(path->data) == -1)) {
+ (remove(path->AsString()) == -1)) {
return false;
}
return success;
}
-bool Directory::List(const char* dir_name,
- bool recursive,
- bool follow_links,
- DirectoryListing *listing) {
- PathBuffer path;
- if (!path.Add(dir_name)) {
- PostError(listing, dir_name);
- return false;
- }
- return ListRecursively(&path, recursive, follow_links, NULL, listing);
-}
-
-
Directory::ExistsResult Directory::Exists(const char* dir_name) {
struct stat entry_info;
int success = TEMP_FAILURE_RETRY(stat(dir_name, &entry_info));
@@ -456,9 +368,9 @@
// The return value must be freed by the caller.
PathBuffer path;
path.Add(const_template);
- if (path.length == 0) {
+ if (path.length() == 0) {
path.Add("/tmp/temp_dir1_");
- } else if ((path.data)[path.length - 1] == '/') {
+ } else if ((path.AsString())[path.length() - 1] == '/') {
path.Add("temp_dir_");
}
if (!path.Add("XXXXXX")) {
@@ -467,14 +379,14 @@
}
char* result;
do {
- result = mkdtemp(path.data);
+ result = mkdtemp(path.AsString());
} while (result == NULL && errno == EINTR);
if (result == NULL) {
return NULL;
}
- int length = strnlen(path.data, PATH_MAX);
+ int length = strnlen(path.AsString(), PATH_MAX);
result = static_cast<char*>(malloc(length + 1));
- strncpy(result, path.data, length);
+ strncpy(result, path.AsString(), length);
result[length] = '\0';
return result;
}
diff --git a/runtime/bin/directory_macos.cc b/runtime/bin/directory_macos.cc
index 3a9ab04..88e430b 100644
--- a/runtime/bin/directory_macos.cc
+++ b/runtime/bin/directory_macos.cc
@@ -21,41 +21,47 @@
namespace dart {
namespace bin {
-class PathBuffer {
- public:
- PathBuffer() : length(0) {
- data = new char[PATH_MAX + 1];
- }
- ~PathBuffer() {
- delete[] data;
- }
+PathBuffer::PathBuffer() : length_(0) {
+ data_ = new char[PATH_MAX + 1];
+}
- char* data;
- int length;
+bool PathBuffer::AddW(const wchar_t* name) {
+ UNREACHABLE();
+ return false;
+}
- bool Add(const char* name) {
- int written = snprintf(data + length,
- PATH_MAX - length,
- "%s",
- name);
- data[PATH_MAX] = '\0';
- if (written <= PATH_MAX - length &&
- written >= 0 &&
- static_cast<size_t>(written) == strlen(name)) {
- length += written;
- return true;
- } else {
- errno = ENAMETOOLONG;
- return false;
- }
- }
+char* PathBuffer::AsString() const {
+ return reinterpret_cast<char*>(data_);
+}
- void Reset(int new_length) {
- length = new_length;
- data[length] = '\0';
+wchar_t* PathBuffer::AsStringW() const {
+ UNREACHABLE();
+ return NULL;
+}
+
+bool PathBuffer::Add(const char* name) {
+ char* data = AsString();
+ int written = snprintf(data + length_,
+ PATH_MAX - length_,
+ "%s",
+ name);
+ data[PATH_MAX] = '\0';
+ if (written <= PATH_MAX - length_ &&
+ written >= 0 &&
+ static_cast<size_t>(written) == strlen(name)) {
+ length_ += written;
+ return true;
+ } else {
+ errno = ENAMETOOLONG;
+ return false;
}
-};
+}
+
+void PathBuffer::Reset(int new_length) {
+ length_ = new_length;
+ AsString()[length_] = '\0';
+}
// A linked list of symbolic links, with their unique file system identifiers.
@@ -67,110 +73,54 @@
};
-// Forward declarations.
-static bool ListRecursively(PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing* listing);
-static bool DeleteRecursively(PathBuffer* path);
-
-
-static void PostError(DirectoryListing *listing,
- const char* dir_name) {
- listing->HandleError(dir_name);
-}
-
-
-static bool HandleDir(char* dir_name,
- PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing *listing) {
- if (strcmp(dir_name, ".") == 0) return true;
- if (strcmp(dir_name, "..") == 0) return true;
- if (!path->Add(dir_name)) {
- PostError(listing, path->data);
- return false;
+ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
+ if (done_) {
+ return kListDone;
}
- return listing->HandleDirectory(path->data) &&
- (!recursive ||
- ListRecursively(path, recursive, follow_links, seen, listing));
-}
+ if (lister_ == 0) {
+ if (!listing->path_buffer().Add(File::PathSeparator())) {
+ done_ = true;
+ return kListError;
+ }
+ path_length_ = listing->path_buffer().length();
+ do {
+ lister_ = reinterpret_cast<intptr_t>(
+ opendir(listing->path_buffer().AsString()));
+ } while (lister_ == 0 && errno == EINTR);
-static bool HandleFile(char* file_name,
- PathBuffer* path,
- DirectoryListing *listing) {
- if (!path->Add(file_name)) {
- PostError(listing, path->data);
- return false;
+ if (lister_ == 0) {
+ done_ = true;
+ return kListError;
+ }
}
- return listing->HandleFile(path->data);
-}
-
-
-static bool HandleLink(char* link_name,
- PathBuffer* path,
- DirectoryListing *listing) {
- if (!path->Add(link_name)) {
- PostError(listing, path->data);
- return false;
- }
- return listing->HandleLink(path->data);
-}
-
-
-static bool ListRecursively(PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing *listing) {
- if (!path->Add(File::PathSeparator())) {
- PostError(listing, path->data);
- return false;
- }
- DIR* dir_pointer;
- do {
- dir_pointer = opendir(path->data);
- } while (dir_pointer == NULL && errno == EINTR);
- if (dir_pointer == NULL) {
- PostError(listing, path->data);
- return false;
- }
+ // Reset.
+ listing->path_buffer().Reset(path_length_);
+ ResetLink();
// Iterate the directory and post the directories and files to the
// ports.
- int path_length = path->length;
int status = 0;
- bool success = true;
dirent entry;
dirent* result;
- while ((status = TEMP_FAILURE_RETRY(readdir_r(dir_pointer,
- &entry,
- &result))) == 0 &&
- result != NULL) {
+ if ((status = TEMP_FAILURE_RETRY(readdir_r(reinterpret_cast<DIR*>(lister_),
+ &entry,
+ &result))) == 0 &&
+ result != NULL) {
+ if (!listing->path_buffer().Add(entry.d_name)) {
+ done_ = true;
+ return kListError;
+ }
switch (entry.d_type) {
case DT_DIR:
- success = HandleDir(entry.d_name,
- path,
- recursive,
- follow_links,
- seen,
- listing) && success;
- break;
+ if (strcmp(entry.d_name, ".") == 0) return Next(listing);
+ if (strcmp(entry.d_name, "..") == 0) return Next(listing);
+ return kListDirectory;
case DT_REG:
- success = HandleFile(entry.d_name,
- path,
- listing) && success;
- break;
+ return kListFile;
case DT_LNK:
- if (!follow_links) {
- success = HandleLink(entry.d_name,
- path,
- listing) && success;
- break;
+ if (!listing->follow_links()) {
+ return kListLink;
}
// Else fall through to next case.
// Fall through.
@@ -180,103 +130,77 @@
// the actual entry type. Notice that stat returns the type of
// the file pointed to.
struct stat entry_info;
- if (!path->Add(entry.d_name)) {
- success = false;
- break;
- }
int stat_success;
- stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
+ stat_success = TEMP_FAILURE_RETRY(
+ lstat(listing->path_buffer().AsString(), &entry_info));
if (stat_success == -1) {
- success = false;
- PostError(listing, path->data);
- break;
+ return kListError;
}
- if (follow_links && S_ISLNK(entry_info.st_mode)) {
+ if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) {
// Check to see if we are in a loop created by a symbolic link.
LinkList current_link = { entry_info.st_dev,
entry_info.st_ino,
- seen };
- LinkList* previous = seen;
- bool looping_link = false;
+ link_ };
+ LinkList* previous = link_;
while (previous != NULL) {
if (previous->dev == current_link.dev &&
previous->ino == current_link.ino) {
// Report the looping link as a link, rather than following it.
- path->Reset(path_length);
- success = HandleLink(entry.d_name,
- path,
- listing) && success;
- looping_link = true;
- break;
+ return kListLink;
}
previous = previous->next;
}
- if (looping_link) break;
- stat_success = TEMP_FAILURE_RETRY(stat(path->data, &entry_info));
+ stat_success = TEMP_FAILURE_RETRY(
+ stat(listing->path_buffer().AsString(), &entry_info));
if (stat_success == -1) {
// Report a broken link as a link, even if follow_links is true.
- path->Reset(path_length);
- success = HandleLink(entry.d_name,
- path,
- listing) && success;
- break;
+ return kListLink;
}
if (S_ISDIR(entry_info.st_mode)) {
// Recurse into the subdirectory with current_link added to the
// linked list of seen file system links.
- path->Reset(path_length);
- success = HandleDir(entry.d_name,
- path,
- recursive,
- follow_links,
- ¤t_link,
- listing) && success;
- break;
+ link_ = new LinkList(current_link);
+ if (strcmp(entry.d_name, ".") == 0) return Next(listing);
+ if (strcmp(entry.d_name, "..") == 0) return Next(listing);
+ return kListDirectory;
}
}
- path->Reset(path_length);
if (S_ISDIR(entry_info.st_mode)) {
- success = HandleDir(entry.d_name,
- path,
- recursive,
- follow_links,
- seen,
- listing) && success;
+ if (strcmp(entry.d_name, ".") == 0) return Next(listing);
+ if (strcmp(entry.d_name, "..") == 0) return Next(listing);
+ return kListDirectory;
} else if (S_ISREG(entry_info.st_mode)) {
- success = HandleFile(entry.d_name,
- path,
- listing) && success;
+ return kListFile;
} else if (S_ISLNK(entry_info.st_mode)) {
- success = HandleLink(entry.d_name,
- path,
- listing) && success;
+ return kListLink;
}
- break;
}
+
default:
break;
}
- path->Reset(path_length);
}
+ done_ = true;
if (status != 0) {
errno = status;
- success = false;
- PostError(listing, path->data);
+ return kListError;
}
- if (closedir(dir_pointer) == -1) {
- success = false;
- PostError(listing, path->data);
+ if (closedir(reinterpret_cast<DIR*>(lister_)) == -1) {
+ return kListError;
}
- return success;
+ return kListDone;
}
+static bool DeleteRecursively(PathBuffer* path);
+
+
static bool DeleteFile(char* file_name,
PathBuffer* path) {
- return path->Add(file_name) && unlink(path->data) == 0;
+ return path->Add(file_name) && unlink(path->AsString()) == 0;
}
@@ -292,10 +216,10 @@
// Do not recurse into links for deletion. Instead delete the link.
// If it's a file, delete it.
struct stat st;
- if (TEMP_FAILURE_RETRY(lstat(path->data, &st)) == -1) {
+ if (TEMP_FAILURE_RETRY(lstat(path->AsString(), &st)) == -1) {
return false;
} else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
- return (unlink(path->data) == 0);
+ return (unlink(path->AsString()) == 0);
}
if (!path->Add(File::PathSeparator())) return false;
@@ -304,7 +228,7 @@
// directory.
DIR* dir_pointer;
do {
- dir_pointer = opendir(path->data);
+ dir_pointer = opendir(path->AsString());
} while (dir_pointer == NULL && errno == EINTR);
if (dir_pointer == NULL) {
@@ -312,7 +236,7 @@
}
// Iterate the directory and delete all files and directories.
- int path_length = path->length;
+ int path_length = path->length();
int read = 0;
bool success = true;
dirent entry;
@@ -342,7 +266,8 @@
success = false;
break;
}
- int lstat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
+ int lstat_success = TEMP_FAILURE_RETRY(
+ lstat(path->AsString(), &entry_info));
if (lstat_success == -1) {
success = false;
break;
@@ -366,26 +291,13 @@
if ((read != 0) ||
(closedir(dir_pointer) == -1) ||
- (remove(path->data) == -1)) {
+ (remove(path->AsString()) == -1)) {
return false;
}
return success;
}
-bool Directory::List(const char* dir_name,
- bool recursive,
- bool follow_links,
- DirectoryListing *listing) {
- PathBuffer path;
- if (!path.Add(dir_name)) {
- PostError(listing, dir_name);
- return false;
- }
- return ListRecursively(&path, recursive, follow_links, NULL, listing);
-}
-
-
Directory::ExistsResult Directory::Exists(const char* dir_name) {
struct stat entry_info;
int success = TEMP_FAILURE_RETRY(stat(dir_name, &entry_info));
@@ -445,9 +357,9 @@
// The return value must be freed by the caller.
PathBuffer path;
path.Add(const_template);
- if (path.length == 0) {
+ if (path.length() == 0) {
path.Add("/tmp/temp_dir1_");
- } else if ((path.data)[path.length - 1] == '/') {
+ } else if ((path.AsString())[path.length() - 1] == '/') {
path.Add("temp_dir_");
}
if (!path.Add("XXXXXX")) {
@@ -456,14 +368,14 @@
}
char* result;
do {
- result = mkdtemp(path.data);
+ result = mkdtemp(path.AsString());
} while (result == NULL && errno == EINTR);
if (result == NULL) {
return NULL;
}
- int length = strlen(path.data);
+ int length = strlen(path.AsString());
result = static_cast<char*>(malloc(length + 1));
- strncpy(result, path.data, length);
+ strncpy(result, path.AsString(), length);
result[length] = '\0';
return result;
}
diff --git a/runtime/bin/directory_win.cc b/runtime/bin/directory_win.cc
index 8f97f0e..9e3cf30 100644
--- a/runtime/bin/directory_win.cc
+++ b/runtime/bin/directory_win.cc
@@ -19,41 +19,47 @@
namespace dart {
namespace bin {
-class PathBuffer {
- public:
- PathBuffer() : length(0) {
- data = new wchar_t[MAX_PATH + 1];
- }
+PathBuffer::PathBuffer() : length_(0) {
+ data_ = new wchar_t[MAX_PATH + 1];
+}
- ~PathBuffer() {
- delete[] data;
- }
+char* PathBuffer::AsString() const {
+ return StringUtils::WideToUtf8(AsStringW());
+}
- wchar_t* data;
- int length;
+wchar_t* PathBuffer::AsStringW() const {
+ return reinterpret_cast<wchar_t*>(data_);
+}
- bool Add(const wchar_t* name) {
- int written = _snwprintf(data + length,
- MAX_PATH - length,
- L"%s",
- name);
- data[MAX_PATH] = L'\0';
- if (written <= MAX_PATH - length &&
- written >= 0 &&
- static_cast<size_t>(written) == wcsnlen(name, MAX_PATH + 1)) {
- length += written;
- return true;
- } else {
- SetLastError(ERROR_BUFFER_OVERFLOW);
- return false;
- }
- }
+bool PathBuffer::Add(const char* name) {
+ const wchar_t* wide_name = StringUtils::Utf8ToWide(name);
+ bool success = AddW(wide_name);
+ free(const_cast<wchar_t*>(wide_name));
+ return success;
+}
- void Reset(int new_length) {
- length = new_length;
- data[length] = L'\0';
+bool PathBuffer::AddW(const wchar_t* name) {
+ wchar_t* data = AsStringW();
+ int written = _snwprintf(data + length_,
+ MAX_PATH - length_,
+ L"%s",
+ name);
+ data[MAX_PATH] = L'\0';
+ if (written <= MAX_PATH - length_ &&
+ written >= 0 &&
+ static_cast<size_t>(written) == wcsnlen(name, MAX_PATH + 1)) {
+ length_ += written;
+ return true;
+ } else {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return false;
}
-};
+}
+
+void PathBuffer::Reset(int new_length) {
+ length_ = new_length;
+ AsStringW()[length_] = L'\0';
+}
// If link_name points to a link, IsBrokenLink will return true if link_name
// points to an invalid target.
@@ -84,96 +90,31 @@
};
// Forward declarations.
-static bool ListRecursively(PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing* listing);
static bool DeleteRecursively(PathBuffer* path);
-static void PostError(DirectoryListing* listing,
- const wchar_t* dir_name) {
- const char* utf8_path = StringUtils::WideToUtf8(dir_name);
- listing->HandleError(utf8_path);
- free(const_cast<char*>(utf8_path));
-}
-
-
-static bool HandleDir(wchar_t* dir_name,
- PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing* listing) {
- if (wcscmp(dir_name, L".") == 0) return true;
- if (wcscmp(dir_name, L"..") == 0) return true;
- if (!path->Add(dir_name)) {
- PostError(listing, path->data);
- return false;
+static ListType HandleFindFile(DirectoryListing* listing,
+ DirectoryListingEntry* entry,
+ WIN32_FIND_DATAW& find_file_data) {
+ if (!listing->path_buffer().AddW(find_file_data.cFileName)) {
+ return kListError;
}
- char* utf8_path = StringUtils::WideToUtf8(path->data);
- bool ok = listing->HandleDirectory(utf8_path);
- free(utf8_path);
- return ok &&
- (!recursive ||
- ListRecursively(path, recursive, follow_links, seen, listing));
-}
-
-
-static bool HandleFile(wchar_t* file_name,
- PathBuffer* path,
- DirectoryListing* listing) {
- if (!path->Add(file_name)) {
- PostError(listing, path->data);
- return false;
- }
- char* utf8_path = StringUtils::WideToUtf8(path->data);
- bool ok = listing->HandleFile(utf8_path);
- free(utf8_path);
- return ok;
-}
-
-
-static bool HandleLink(wchar_t* link_name,
- PathBuffer* path,
- DirectoryListing* listing) {
- if (!path->Add(link_name)) {
- PostError(listing, path->data);
- return false;
- }
- char* utf8_path = StringUtils::WideToUtf8(path->data);
- bool ok = listing->HandleLink(utf8_path);
- free(utf8_path);
- return ok;
-}
-
-
-static bool HandleEntry(LPWIN32_FIND_DATAW find_file_data,
- PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing* listing) {
- DWORD attributes = find_file_data->dwFileAttributes;
+ DWORD attributes = find_file_data.dwFileAttributes;
if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
- if (!follow_links) {
- return HandleLink(find_file_data->cFileName, path, listing);
+ if (!listing->follow_links()) {
+ return kListLink;
}
- int path_length = path->length;
- if (!path->Add(find_file_data->cFileName)) return false;
HANDLE handle = CreateFileW(
- path->data,
+ listing->path_buffer().AsStringW(),
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
- path->Reset(path_length);
if (handle == INVALID_HANDLE_VALUE) {
// Report as (broken) link.
- return HandleLink(find_file_data->cFileName, path, listing);
+ return kListLink;
}
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
// Check the seen link targets to see if we are in a file system loop.
@@ -184,103 +125,99 @@
DWORD error = GetLastError();
CloseHandle(handle);
SetLastError(error);
- PostError(listing, path->data);
- return false;
+ return kListError;
}
CloseHandle(handle);
current_link.volume = info.dwVolumeSerialNumber;
current_link.id_low = info.nFileIndexLow;
current_link.id_high = info.nFileIndexHigh;
- current_link.next = seen;
- LinkList* previous = seen;
+ current_link.next = entry->link();
+ LinkList* previous = entry->link();
while (previous != NULL) {
if (previous->volume == current_link.volume &&
previous->id_low == current_link.id_low &&
previous->id_high == current_link.id_high) {
// Report the looping link as a link, rather than following it.
- return HandleLink(find_file_data->cFileName, path, listing);
+ return kListLink;
}
previous = previous->next;
}
// Recurse into the directory, adding current link to the seen links list.
- return HandleDir(find_file_data->cFileName,
- path,
- recursive,
- follow_links,
- ¤t_link,
- listing);
+ if (wcscmp(find_file_data.cFileName, L".") == 0 ||
+ wcscmp(find_file_data.cFileName, L"..") == 0) {
+ return entry->Next(listing);
+ }
+ entry->set_link(new LinkList(current_link));
+ return kListDirectory;
}
}
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
- return HandleDir(find_file_data->cFileName,
- path,
- recursive,
- follow_links,
- seen,
- listing);
+ if (wcscmp(find_file_data.cFileName, L".") == 0 ||
+ wcscmp(find_file_data.cFileName, L"..") == 0) {
+ return entry->Next(listing);
+ }
+ return kListDirectory;
} else {
- return HandleFile(find_file_data->cFileName, path, listing);
+ return kListFile;
}
}
-
-static bool ListRecursively(PathBuffer* path,
- bool recursive,
- bool follow_links,
- LinkList* seen,
- DirectoryListing* listing) {
- if (!path->Add(L"\\*")) {
- PostError(listing, path->data);
- return false;
+ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
+ if (done_) {
+ return kListDone;
}
WIN32_FIND_DATAW find_file_data;
- HANDLE find_handle = FindFirstFileW(path->data, &find_file_data);
- // Adjust the path by removing the '*' used for the search.
- path->Reset(path->length - 1);
+ if (lister_ == 0) {
+ if (!listing->path_buffer().AddW(L"\\*")) {
+ done_ = true;
+ return kListError;
+ }
- if (find_handle == INVALID_HANDLE_VALUE) {
- PostError(listing, path->data);
- return false;
+ path_length_ = listing->path_buffer().length() - 1;
+
+ HANDLE find_handle = FindFirstFileW(listing->path_buffer().AsStringW(),
+ &find_file_data);
+
+ if (find_handle == INVALID_HANDLE_VALUE) {
+ done_ = true;
+ return kListError;
+ }
+
+ lister_ = reinterpret_cast<intptr_t>(find_handle);
+
+ listing->path_buffer().Reset(path_length_);
+
+ return HandleFindFile(listing, this, find_file_data);
}
- int path_length = path->length;
- bool success = HandleEntry(&find_file_data,
- path,
- recursive,
- follow_links,
- seen,
- listing);
+ // Reset.
+ listing->path_buffer().Reset(path_length_);
+ ResetLink();
- while ((FindNextFileW(find_handle, &find_file_data) != 0)) {
- path->Reset(path_length); // HandleEntry adds the entry name to path.
- success = HandleEntry(&find_file_data,
- path,
- recursive,
- follow_links,
- seen,
- listing) && success;
+ if (FindNextFileW(reinterpret_cast<HANDLE>(lister_), &find_file_data) != 0) {
+ return HandleFindFile(listing, this, find_file_data);
}
+ done_ = true;
+
if (GetLastError() != ERROR_NO_MORE_FILES) {
- success = false;
- PostError(listing, path->data);
+ return kListError;
}
- if (FindClose(find_handle) == 0) {
- success = false;
- PostError(listing, path->data);
+ if (FindClose(reinterpret_cast<HANDLE>(lister_)) == 0) {
+ return kListError;
}
- return success;
+ return kListDone;
}
static bool DeleteFile(wchar_t* file_name, PathBuffer* path) {
- if (!path->Add(file_name)) return false;
+ if (!path->AddW(file_name)) return false;
- if (DeleteFileW(path->data) != 0) {
+ if (DeleteFileW(path->AsStringW()) != 0) {
return true;
}
@@ -288,7 +225,7 @@
// again. This mirrors Linux/Mac where a directory containing read-only files
// can still be recursively deleted.
if (GetLastError() == ERROR_ACCESS_DENIED) {
- DWORD attributes = GetFileAttributesW(path->data);
+ DWORD attributes = GetFileAttributesW(path->AsStringW());
if (attributes == INVALID_FILE_ATTRIBUTES) {
return false;
}
@@ -296,11 +233,11 @@
if ((attributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY) {
attributes &= ~FILE_ATTRIBUTE_READONLY;
- if (SetFileAttributesW(path->data, attributes) == 0) {
+ if (SetFileAttributesW(path->AsStringW(), attributes) == 0) {
return false;
}
- return DeleteFileW(path->data) != 0;
+ return DeleteFileW(path->AsStringW()) != 0;
}
}
@@ -311,7 +248,7 @@
static bool DeleteDir(wchar_t* dir_name, PathBuffer* path) {
if (wcscmp(dir_name, L".") == 0) return true;
if (wcscmp(dir_name, L"..") == 0) return true;
- return path->Add(dir_name) && DeleteRecursively(path);
+ return path->AddW(dir_name) && DeleteRecursively(path);
}
@@ -327,7 +264,7 @@
static bool DeleteRecursively(PathBuffer* path) {
- DWORD attributes = GetFileAttributesW(path->data);
+ DWORD attributes = GetFileAttributesW(path->AsStringW());
if ((attributes == INVALID_FILE_ATTRIBUTES)) {
return false;
}
@@ -335,20 +272,20 @@
// filesystem that we do not want to recurse into.
if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
// Just delete the junction itself.
- return RemoveDirectoryW(path->data) != 0;
+ return RemoveDirectoryW(path->AsStringW()) != 0;
}
// If it's a file, remove it directly.
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
return DeleteFile(L"", path);
}
- if (!path->Add(L"\\*")) return false;
+ if (!path->AddW(L"\\*")) return false;
WIN32_FIND_DATAW find_file_data;
- HANDLE find_handle = FindFirstFileW(path->data, &find_file_data);
+ HANDLE find_handle = FindFirstFileW(path->AsStringW(), &find_file_data);
// Adjust the path by removing the '*' used for the search.
- int path_length = path->length - 1;
+ int path_length = path->length() - 1;
path->Reset(path_length);
if (find_handle == INVALID_HANDLE_VALUE) {
@@ -365,7 +302,7 @@
path->Reset(path_length - 1); // Drop the "\" from the end of the path.
if ((GetLastError() != ERROR_NO_MORE_FILES) ||
(FindClose(find_handle) == 0) ||
- (RemoveDirectoryW(path->data) == 0)) {
+ (RemoveDirectoryW(path->AsStringW()) == 0)) {
return false;
}
@@ -373,21 +310,6 @@
}
-bool Directory::List(const char* dir_name,
- bool recursive,
- bool follow_links,
- DirectoryListing* listing) {
- const wchar_t* system_name = StringUtils::Utf8ToWide(dir_name);
- PathBuffer path;
- if (!path.Add(system_name)) {
- PostError(listing, system_name);
- return false;
- }
- free(const_cast<wchar_t*>(system_name));
- return ListRecursively(&path, recursive, follow_links, NULL, listing);
-}
-
-
static Directory::ExistsResult ExistsHelper(const wchar_t* dir_name) {
DWORD attributes = GetFileAttributesW(dir_name);
if (attributes == INVALID_FILE_ATTRIBUTES) {
@@ -456,22 +378,22 @@
// The return value must be freed by the caller.
PathBuffer path;
if (0 == strncmp(const_template, "", 1)) {
- path.length = GetTempPathW(MAX_PATH, path.data);
- if (path.length == 0) {
+ path.Reset(GetTempPathW(MAX_PATH, path.AsStringW()));
+ if (path.length() == 0) {
return NULL;
}
} else {
const wchar_t* system_template = StringUtils::Utf8ToWide(const_template);
- path.Add(system_template);
+ path.AddW(system_template);
free(const_cast<wchar_t*>(system_template));
}
// Length of tempdir-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx is 44.
- if (path.length > MAX_PATH - 44) {
+ if (path.length() > MAX_PATH - 44) {
return NULL;
}
- if ((path.data)[path.length - 1] == L'\\') {
+ if ((path.AsStringW())[path.length() - 1] == L'\\') {
// No base name for the directory - use "tempdir".
- path.Add(L"tempdir");
+ path.AddW(L"tempdir");
}
UUID uuid;
@@ -485,14 +407,14 @@
return NULL;
}
- path.Add(L"-");
+ path.AddW(L"-");
// RPC_WSTR is an unsigned short*, so we cast to wchar_t*.
- path.Add(reinterpret_cast<wchar_t*>(uuid_string));
+ path.AddW(reinterpret_cast<wchar_t*>(uuid_string));
RpcStringFreeW(&uuid_string);
- if (!CreateDirectoryW(path.data, NULL)) {
+ if (!CreateDirectoryW(path.AsStringW(), NULL)) {
return NULL;
}
- char* result = StringUtils::WideToUtf8(path.data);
+ char* result = path.AsString();
return result;
}
@@ -508,7 +430,7 @@
}
} else {
PathBuffer path;
- if (path.Add(system_dir_name)) {
+ if (path.AddW(system_dir_name)) {
result = DeleteRecursively(&path);
}
}
diff --git a/runtime/bin/native_service.h b/runtime/bin/native_service.h
index ff1b0bf..c001750 100644
--- a/runtime/bin/native_service.h
+++ b/runtime/bin/native_service.h
@@ -6,6 +6,7 @@
#define BIN_NATIVE_SERVICE_H_
#include "include/dart_api.h"
+#include "include/dart_native_api.h"
#include "platform/globals.h"
#include "platform/thread.h"
diff --git a/runtime/dart-runtime.gyp b/runtime/dart-runtime.gyp
index 795e3d4..b00105d 100644
--- a/runtime/dart-runtime.gyp
+++ b/runtime/dart-runtime.gyp
@@ -50,8 +50,12 @@
'sources': [
'include/dart_api.h',
'include/dart_debugger_api.h',
+ 'include/dart_mirrors_api.h',
+ 'include/dart_native_api.h',
'vm/dart_api_impl.cc',
'vm/debugger_api_impl.cc',
+ 'vm/mirrors_api_impl.cc',
+ 'vm/native_api_impl.cc',
'vm/version.h',
'<(version_cc_file)',
],
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index 075ce12..cd09095b 100755
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -65,7 +65,11 @@
#include <assert.h>
-/* --- Handles --- */
+/*
+ * =======
+ * Handles
+ * =======
+ */
/**
* An object reference managed by the Dart VM garbage collector.
@@ -483,7 +487,12 @@
Dart_WeakPersistentHandle* values,
intptr_t num_values);
-/* --- Garbage Collection Callbacks --- */
+
+/*
+ * ============================
+ * Garbage Collection Callbacks
+ * ============================
+ */
/**
* Callbacks signal the beginning and end of a garbage collection.
@@ -553,7 +562,12 @@
DART_EXPORT Dart_Handle Dart_RemoveGcEpilogueCallback(
Dart_GcEpilogueCallback callback);
-/* --- Initialization and Globals --- */
+
+/*
+ * ==========================
+ * Initialization and Globals
+ * ==========================
+ */
/**
* Gets the version string for the Dart VM.
@@ -744,7 +758,12 @@
*/
DART_EXPORT bool Dart_IsVMFlagSet(const char* flag_name);
-/* --- Isolates --- */
+
+/*
+ * ========
+ * Isolates
+ * ========
+ */
/**
* Creates a new isolate. The new isolate becomes the current isolate.
@@ -865,7 +884,6 @@
DART_EXPORT Dart_Handle Dart_CreateScriptSnapshot(uint8_t** buffer,
intptr_t* size);
-
/**
* Schedules an interrupt for the specified isolate.
*
@@ -880,7 +898,6 @@
*/
DART_EXPORT void Dart_InterruptIsolate(Dart_Isolate isolate);
-
/**
* Make isolate runnable.
*
@@ -894,7 +911,11 @@
DART_EXPORT bool Dart_IsolateMakeRunnable(Dart_Isolate isolate);
-/* --- Messages and Ports --- */
+/*
+ * ==================
+ * Messages and Ports
+ * ==================
+ */
/**
* A port is used to send or receive inter-isolate messages
@@ -996,7 +1017,12 @@
*/
DART_EXPORT Dart_Handle Dart_GetReceivePort(Dart_Port port_id);
-/* --- Scopes ---- */
+
+/*
+ * ======
+ * Scopes
+ * ======
+ */
/**
* Enters a new scope.
@@ -1042,7 +1068,12 @@
*/
DART_EXPORT uint8_t* Dart_ScopeAllocate(intptr_t size);
-/* --- Objects ---- */
+
+/*
+ * =======
+ * Objects
+ * =======
+ */
/**
* Returns the null object.
@@ -1093,7 +1124,39 @@
Dart_Handle type,
bool* instanceof);
-/* --- Instances ----
+
+/**
+ * Query object type.
+ *
+ * \param object Some Object.
+ *
+ * \return true if Object is of the specified type.
+ */
+DART_EXPORT bool Dart_IsInstance(Dart_Handle object);
+DART_EXPORT bool Dart_IsNumber(Dart_Handle object);
+DART_EXPORT bool Dart_IsInteger(Dart_Handle object);
+DART_EXPORT bool Dart_IsDouble(Dart_Handle object);
+DART_EXPORT bool Dart_IsBoolean(Dart_Handle object);
+DART_EXPORT bool Dart_IsString(Dart_Handle object);
+DART_EXPORT bool Dart_IsStringLatin1(Dart_Handle object); /* (ISO-8859-1) */
+DART_EXPORT bool Dart_IsExternalString(Dart_Handle object);
+DART_EXPORT bool Dart_IsList(Dart_Handle object);
+DART_EXPORT bool Dart_IsLibrary(Dart_Handle object);
+DART_EXPORT bool Dart_IsClass(Dart_Handle handle);
+DART_EXPORT bool Dart_IsAbstractClass(Dart_Handle handle);
+DART_EXPORT bool Dart_IsFunction(Dart_Handle handle);
+DART_EXPORT bool Dart_IsVariable(Dart_Handle handle);
+DART_EXPORT bool Dart_IsTypeVariable(Dart_Handle handle);
+DART_EXPORT bool Dart_IsClosure(Dart_Handle object);
+
+
+/*
+ * =========
+ * Instances
+ * =========
+ */
+
+/*
* For the purposes of the embedding api, not all objects returned are
* Dart language objects. Within the api, we use the term 'Instance'
* to indicate handles which refer to true Dart language objects.
@@ -1102,11 +1165,6 @@
* any functions that more properly belong here. */
/**
- * Does this handle refer to some Dart language object?
- */
-DART_EXPORT bool Dart_IsInstance(Dart_Handle object);
-
-/**
* Gets the class for some Dart language object.
*
* \param instance Some Dart object.
@@ -1116,19 +1174,12 @@
*/
DART_EXPORT Dart_Handle Dart_InstanceGetClass(Dart_Handle instance);
-/* --- Numbers ---- */
-/**
- * Is this object a Number?
+/*
+ * =============================
+ * Numbers, Integers and Doubles
+ * =============================
*/
-DART_EXPORT bool Dart_IsNumber(Dart_Handle object);
-
-/* --- Integers ---- */
-
-/**
- * Is this object an Integer?
- */
-DART_EXPORT bool Dart_IsInteger(Dart_Handle object);
/**
* Does this Integer fit into a 64-bit signed integer?
@@ -1213,7 +1264,32 @@
DART_EXPORT Dart_Handle Dart_IntegerToHexCString(Dart_Handle integer,
const char** value);
-/* --- Booleans ---- */
+/**
+ * Returns a Double with the provided value.
+ *
+ * \param value A double.
+ *
+ * \return The Double object if no error occurs. Otherwise returns
+ * an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewDouble(double value);
+
+/**
+ * Gets the value of a Double
+ *
+ * \param double_obj A Double
+ * \param value Returns the value of the Double.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj, double* value);
+
+
+/*
+ * ========
+ * Booleans
+ * ========
+ */
/**
* Returns the True object.
@@ -1234,11 +1310,6 @@
DART_EXPORT Dart_Handle Dart_False();
/**
- * Is this object a Boolean?
- */
-DART_EXPORT bool Dart_IsBoolean(Dart_Handle object);
-
-/**
* Returns a Boolean with the provided value.
*
* \param value true or false.
@@ -1258,44 +1329,12 @@
*/
DART_EXPORT Dart_Handle Dart_BooleanValue(Dart_Handle boolean_obj, bool* value);
-/* --- Doubles --- */
-/**
- * Is this object a Double?
+/*
+ * =======
+ * Strings
+ * =======
*/
-DART_EXPORT bool Dart_IsDouble(Dart_Handle object);
-
-/**
- * Returns a Double with the provided value.
- *
- * \param value A double.
- *
- * \return The Double object if no error occurs. Otherwise returns
- * an error handle.
- */
-DART_EXPORT Dart_Handle Dart_NewDouble(double value);
-
-/**
- * Gets the value of a Double
- *
- * \param double_obj A Double
- * \param value Returns the value of the Double.
- *
- * \return A valid handle if no error occurs during the operation.
- */
-DART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj, double* value);
-
-/* --- Strings --- */
-
-/**
- * Is this object a String?
- */
-DART_EXPORT bool Dart_IsString(Dart_Handle object);
-
-/**
- * Is this object a Latin-1 (ISO-8859-1) String?
- */
-DART_EXPORT bool Dart_IsStringLatin1(Dart_Handle object);
/**
* Gets the length of a String.
@@ -1359,14 +1398,6 @@
intptr_t length);
/**
- * Is this object an external String?
- *
- * An external String is a String which references a fixed array of
- * codepoints which is external to the Dart heap.
- */
-DART_EXPORT bool Dart_IsExternalString(Dart_Handle object);
-
-/**
* Retrieves the peer pointer associated with an external String.
*/
DART_EXPORT Dart_Handle Dart_ExternalStringGetPeer(Dart_Handle object,
@@ -1512,12 +1543,11 @@
Dart_PeerFinalizer cback);
-/* --- Lists --- */
-
-/**
- * Is this object a List?
+/*
+ * =====
+ * Lists
+ * =====
*/
-DART_EXPORT bool Dart_IsList(Dart_Handle object);
/**
* Returns a List of the desired length.
@@ -1590,7 +1620,12 @@
uint8_t* native_array,
intptr_t length);
-/* --- Typed Data --- */
+
+/*
+ * ==========
+ * Typed Data
+ * ==========
+ */
typedef enum {
Dart_TypedData_kByteData = 0,
@@ -1689,378 +1724,11 @@
DART_EXPORT Dart_Handle Dart_TypedDataReleaseData(Dart_Handle object);
-/* --- Closures --- */
-
-/**
- * Is this object a Closure?
+/*
+ * ============================================================
+ * Invoking Constructors, Methods, Closures and Field accessors
+ * ============================================================
*/
-DART_EXPORT bool Dart_IsClosure(Dart_Handle object);
-
-/**
- * Retrieves the function of a closure.
- *
- * \return A handle to the function of the closure, or an error handle if the
- * argument is not a closure.
- */
-DART_EXPORT Dart_Handle Dart_ClosureFunction(Dart_Handle closure);
-
-/**
- * Invokes a Closure with the given arguments.
- *
- * May generate an unhandled exception error.
- *
- * \return If no error occurs during execution, then the result of
- * invoking the closure is returned. If an error occurs during
- * execution, then an error handle is returned.
- */
-DART_EXPORT Dart_Handle Dart_InvokeClosure(Dart_Handle closure,
- int number_of_arguments,
- Dart_Handle* arguments);
-
-/* --- Classes and Interfaces --- */
-
-/**
- * Is this a class handle?
- */
-DART_EXPORT bool Dart_IsClass(Dart_Handle handle);
-
-/**
- * Is this an abstract class handle?
- */
-DART_EXPORT bool Dart_IsAbstractClass(Dart_Handle handle);
-
-/**
- * Returns the class name for the provided class or interface.
- */
-DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle clazz);
-
-/**
- * Returns the library for the provided class or interface.
- */
-DART_EXPORT Dart_Handle Dart_ClassGetLibrary(Dart_Handle clazz);
-
-/**
- * Returns the number of interfaces directly implemented by some class
- * or interface.
- *
- * TODO(turnidge): Finish documentation.
- */
-DART_EXPORT Dart_Handle Dart_ClassGetInterfaceCount(Dart_Handle clazz,
- intptr_t* count);
-
-/**
- * Returns the interface at some index in the list of interfaces some
- * class or inteface.
- *
- * TODO(turnidge): Finish documentation.
- */
-DART_EXPORT Dart_Handle Dart_ClassGetInterfaceAt(Dart_Handle clazz,
- intptr_t index);
-
-/**
- * Is this class defined by a typedef?
- *
- * Typedef definitions from the main program are represented as a
- * special kind of class handle. See Dart_ClassGetTypedefReferent.
- *
- * TODO(turnidge): Finish documentation.
- */
-DART_EXPORT bool Dart_ClassIsTypedef(Dart_Handle clazz);
-
-/**
- * Returns a handle to the type to which a typedef refers.
- *
- * It is an error to call this function on a handle for which
- * Dart_ClassIsTypedef is not true.
- *
- * TODO(turnidge): Finish documentation.
- */
-DART_EXPORT Dart_Handle Dart_ClassGetTypedefReferent(Dart_Handle clazz);
-
-/**
- * Does this class represent the type of a function?
- */
-DART_EXPORT bool Dart_ClassIsFunctionType(Dart_Handle clazz);
-
-/**
- * Returns a function handle representing the signature associated
- * with a function type.
- *
- * The return value is a function handle (See Dart_IsFunction, etc.).
- *
- * TODO(turnidge): Finish documentation.
- */
-DART_EXPORT Dart_Handle Dart_ClassGetFunctionTypeSignature(Dart_Handle clazz);
-
-/* --- Function and Variable Declarations --- */
-
-/**
- * Returns a list of the names of all functions or methods declared in
- * a library or class.
- *
- * \param target A library or class.
- *
- * \return If no error occurs, a list of strings is returned.
- * Otherwise an error handle is returned.
- */
-DART_EXPORT Dart_Handle Dart_GetFunctionNames(Dart_Handle target);
-
-/**
- * Looks up a function or method declaration by name from a library or
- * class.
- *
- * \param target The library or class containing the function.
- * \param function_name The name of the function.
- *
- * \return If an error is encountered, returns an error handle.
- * Otherwise returns a function handle if the function is found of
- * Dart_Null() if the function is not found.
- */
-DART_EXPORT Dart_Handle Dart_LookupFunction(Dart_Handle target,
- Dart_Handle function_name);
-
-/**
- * Is this a function or method declaration handle?
- */
-DART_EXPORT bool Dart_IsFunction(Dart_Handle handle);
-
-/**
- * Returns the name for the provided function or method.
- *
- * \return A valid string handle if no error occurs during the
- * operation.
- */
-DART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function);
-
-/**
- * Returns a handle to the owner of a function.
- *
- * The owner of an instance method or a static method is its defining
- * class. The owner of a top-level function is its defining
- * library. The owner of the function of a non-implicit closure is the
- * function of the method or closure that defines the non-implicit
- * closure.
- *
- * \return A valid handle to the owner of the function, or an error
- * handle if the argument is not a valid handle to a function.
- */
-DART_EXPORT Dart_Handle Dart_FunctionOwner(Dart_Handle function);
-
-/**
- * Determines whether a function handle refers to an abstract method.
- *
- * \param function A handle to a function or method declaration.
- * \param is_static Returns whether the handle refers to an abstract method.
- *
- * \return A valid handle if no error occurs during the operation.
- */
-DART_EXPORT Dart_Handle Dart_FunctionIsAbstract(Dart_Handle function,
- bool* is_abstract);
-
-/**
- * Determines whether a function handle referes to a static function
- * of method.
- *
- * For the purposes of the embedding API, a top-level function is
- * implicitly declared static.
- *
- * \param function A handle to a function or method declaration.
- * \param is_static Returns whether the function or method is declared static.
- *
- * \return A valid handle if no error occurs during the operation.
- */
-DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function,
- bool* is_static);
-
-/**
- * Determines whether a function handle referes to a constructor.
- *
- * \param function A handle to a function or method declaration.
- * \param is_static Returns whether the function or method is a constructor.
- *
- * \return A valid handle if no error occurs during the operation.
- */
-DART_EXPORT Dart_Handle Dart_FunctionIsConstructor(Dart_Handle function,
- bool* is_constructor);
-/* TODO(turnidge): Document behavior for factory constructors too. */
-
-/**
- * Determines whether a function or method is a getter.
- *
- * \param function A handle to a function or method declaration.
- * \param is_static Returns whether the function or method is a getter.
- *
- * \return A valid handle if no error occurs during the operation.
- */
-DART_EXPORT Dart_Handle Dart_FunctionIsGetter(Dart_Handle function,
- bool* is_getter);
-
-/**
- * Determines whether a function or method is a setter.
- *
- * \param function A handle to a function or method declaration.
- * \param is_static Returns whether the function or method is a setter.
- *
- * \return A valid handle if no error occurs during the operation.
- */
-DART_EXPORT Dart_Handle Dart_FunctionIsSetter(Dart_Handle function,
- bool* is_setter);
-
-/**
- * Returns the return type of a function.
- *
- * \return A valid handle to a type or an error handle if the argument
- * is not valid.
- */
-DART_EXPORT Dart_Handle Dart_FunctionReturnType(Dart_Handle function);
-
-/**
- * Determines the number of required and optional parameters.
- *
- * \param function A handle to a function or method declaration.
- * \param fixed_param_count Returns the number of required parameters.
- * \param opt_param_count Returns the number of optional parameters.
- *
- * \return A valid handle if no error occurs during the operation.
- */
-DART_EXPORT Dart_Handle Dart_FunctionParameterCounts(
- Dart_Handle function,
- int64_t* fixed_param_count,
- int64_t* opt_param_count);
-
-/**
- * Returns a handle to the type of a function parameter.
- *
- * \return A valid handle to a type or an error handle if the argument
- * is not valid.
- */
-DART_EXPORT Dart_Handle Dart_FunctionParameterType(Dart_Handle function,
- int parameter_index);
-
-/**
- * Returns a list of the names of all variables declared in a library
- * or class.
- *
- * \param target A library or class.
- *
- * \return If no error occurs, a list of strings is returned.
- * Otherwise an error handle is returned.
- */
-DART_EXPORT Dart_Handle Dart_GetVariableNames(Dart_Handle target);
-
-/**
- * Looks up a variable declaration by name from a library or class.
- *
- * \param target The library or class containing the variable.
- * \param variable_name The name of the variable.
- *
- * \return If an error is encountered, returns an error handle.
- * Otherwise returns a variable handle if the variable is found or
- * Dart_Null() if the variable is not found.
- */
-DART_EXPORT Dart_Handle Dart_LookupVariable(Dart_Handle target,
- Dart_Handle variable_name);
-
-/**
- * Is this a variable declaration handle?
- */
-DART_EXPORT bool Dart_IsVariable(Dart_Handle handle);
-
-/**
- * Returns the name for the provided variable.
- */
-DART_EXPORT Dart_Handle Dart_VariableName(Dart_Handle variable);
-
-/**
- * Determines whether a variable is declared static.
- *
- * For the purposes of the embedding API, a top-level variable is
- * implicitly declared static.
- *
- * \param variable A handle to a variable declaration.
- * \param is_static Returns whether the variable is declared static.
- *
- * \return A valid handle if no error occurs during the operation.
- */
-DART_EXPORT Dart_Handle Dart_VariableIsStatic(Dart_Handle variable,
- bool* is_static);
-
-/**
- * Determines whether a variable is declared final.
- *
- * \param variable A handle to a variable declaration.
- * \param is_final Returns whether the variable is declared final.
- *
- * \return A valid handle if no error occurs during the operation.
- */
-DART_EXPORT Dart_Handle Dart_VariableIsFinal(Dart_Handle variable,
- bool* is_final);
-
-/**
- * Returns the type of a variable.
- *
- * \return A valid handle to a type of or an error handle if the
- * argument is not valid.
- */
-DART_EXPORT Dart_Handle Dart_VariableType(Dart_Handle function);
-
-/**
- * Returns a list of the names of all type variables declared in a class.
- *
- * The type variables list preserves the original declaration order.
- *
- * \param clazz A class.
- *
- * \return If no error occurs, a list of strings is returned.
- * Otherwise an error handle is returned.
- */
-DART_EXPORT Dart_Handle Dart_GetTypeVariableNames(Dart_Handle clazz);
-
-/**
- * Looks up a type variable declaration by name from a class.
- *
- * \param clazz The class containing the type variable.
- * \param variable_name The name of the type variable.
- *
- * \return If an error is encountered, returns an error handle.
- * Otherwise returns a type variable handle if the type variable is
- * found or Dart_Null() if the type variable is not found.
- */
-DART_EXPORT Dart_Handle Dart_LookupTypeVariable(Dart_Handle clazz,
- Dart_Handle type_variable_name);
-
-/**
- * Is this a type variable handle?
- */
-DART_EXPORT bool Dart_IsTypeVariable(Dart_Handle handle);
-
-/**
- * Returns the name for the provided type variable.
- */
-DART_EXPORT Dart_Handle Dart_TypeVariableName(Dart_Handle type_variable);
-
-/**
- * Returns the owner of a function.
- *
- * The owner of a type variable is its defining class.
- *
- * \return A valid handle to the owner of the type variable, or an error
- * handle if the argument is not a valid handle to a type variable.
- */
-DART_EXPORT Dart_Handle Dart_TypeVariableOwner(Dart_Handle type_variable);
-
-/**
- * Returns the upper bound of a type variable.
- *
- * The upper bound of a type variable is ...
- *
- * \return A valid handle to a type, or an error handle if the
- * argument is not a valid handle.
- */
-DART_EXPORT Dart_Handle Dart_TypeVariableUpperBound(Dart_Handle type_variable);
-/* TODO(turnidge): Finish documentation. */
-
-/* --- Constructors, Methods, and Fields --- */
/**
* Invokes a constructor, creating a new object.
@@ -2113,6 +1781,19 @@
/* TODO(turnidge): Document how to invoke operators. */
/**
+ * Invokes a Closure with the given arguments.
+ *
+ * May generate an unhandled exception error.
+ *
+ * \return If no error occurs during execution, then the result of
+ * invoking the closure is returned. If an error occurs during
+ * execution, then an error handle is returned.
+ */
+DART_EXPORT Dart_Handle Dart_InvokeClosure(Dart_Handle closure,
+ int number_of_arguments,
+ Dart_Handle* arguments);
+
+/**
* Gets the value of a field.
*
* The 'container' parameter may be an object, class, or library. If
@@ -2157,39 +1838,14 @@
Dart_Handle name,
Dart_Handle value);
-/**
- * Creates a native wrapper class.
- *
- * TODO(turnidge): Document.
- */
-DART_EXPORT Dart_Handle Dart_CreateNativeWrapperClass(Dart_Handle library,
- Dart_Handle class_name,
- int field_count);
-/**
- * Gets the number of native instance fields in an object.
+/*
+ * ==========
+ * Exceptions
+ * ==========
*/
-DART_EXPORT Dart_Handle Dart_GetNativeInstanceFieldCount(Dart_Handle obj,
- int* count);
-/**
- * Gets the value of a native field.
- *
- * TODO(turnidge): Document.
- */
-DART_EXPORT Dart_Handle Dart_GetNativeInstanceField(Dart_Handle obj,
- int index,
- intptr_t* value);
-/**
- * Sets the value of a native field.
- *
- * TODO(turnidge): Document.
- */
-DART_EXPORT Dart_Handle Dart_SetNativeInstanceField(Dart_Handle obj,
- int index,
- intptr_t value);
-
-/* --- Exceptions ----
+/*
* TODO(turnidge): Remove these functions from the api and replace all
* uses with Dart_NewUnhandledExceptionError. */
@@ -2224,7 +1880,45 @@
DART_EXPORT Dart_Handle Dart_RethrowException(Dart_Handle exception,
Dart_Handle stacktrace);
-/* --- Native functions --- */
+
+/*
+ * ===========================
+ * Native fields and functions
+ * ===========================
+ */
+
+/**
+ * Creates a native wrapper class.
+ *
+ * TODO(turnidge): Document.
+ */
+DART_EXPORT Dart_Handle Dart_CreateNativeWrapperClass(Dart_Handle library,
+ Dart_Handle class_name,
+ int field_count);
+
+/**
+ * Gets the number of native instance fields in an object.
+ */
+DART_EXPORT Dart_Handle Dart_GetNativeInstanceFieldCount(Dart_Handle obj,
+ int* count);
+
+/**
+ * Gets the value of a native field.
+ *
+ * TODO(turnidge): Document.
+ */
+DART_EXPORT Dart_Handle Dart_GetNativeInstanceField(Dart_Handle obj,
+ int index,
+ intptr_t* value);
+
+/**
+ * Sets the value of a native field.
+ *
+ * TODO(turnidge): Document.
+ */
+DART_EXPORT Dart_Handle Dart_SetNativeInstanceField(Dart_Handle obj,
+ int index,
+ intptr_t value);
/**
* The arguments to a native function.
@@ -2274,24 +1968,26 @@
/* TODO(turnidge): Consider renaming to NativeFunctionResolver or
* NativeResolver. */
-
-/* --- Metadata support --- */
-
/**
- * Get metadata associated with an object.
+ * Sets the callback used to resolve native functions for a library.
*
- * \param obj Object for which the metadata is retrieved.
+ * \param library A library.
+ * \param resolver A native entry resolver.
*
- * \return If no error occurs, returns an array of metadata values.
- * Returns an empty array if there is no metadata for the object.
- * Returns an error if the evaluation of the metadata expressions fails.
- *
+ * \return A valid handle if the native resolver was set successfully.
*/
-DART_EXPORT Dart_Handle Dart_GetMetadata(Dart_Handle obj);
+DART_EXPORT Dart_Handle Dart_SetNativeResolver(
+ Dart_Handle library,
+ Dart_NativeEntryResolver resolver);
+/* TODO(turnidge): Rename to Dart_LibrarySetNativeResolver? */
-/* --- Scripts and Libraries ---
- * TODO(turnidge): Finish documenting this section. */
+/*
+ * =====================
+ * Scripts and Libraries
+ * =====================
+ */
+/* TODO(turnidge): Finish documenting this section. */
typedef enum {
Dart_kLibraryTag = 0,
@@ -2362,25 +2058,6 @@
DART_EXPORT Dart_Handle Dart_RootLibrary();
/**
- * Forces all loaded classes and functions to be compiled eagerly in
- * the current isolate..
- *
- * TODO(turnidge): Document.
- */
-DART_EXPORT Dart_Handle Dart_CompileAll();
-
-/**
- * Check that all function fingerprints are OK.
- *
- */
-DART_EXPORT Dart_Handle Dart_CheckFunctionFingerprints();
-
-/**
- * Is this object a Library?
- */
-DART_EXPORT bool Dart_IsLibrary(Dart_Handle object);
-
-/**
* Lookup a class or interface by name from a Library.
*
* \param library The library containing the class or interface.
@@ -2395,24 +2072,10 @@
* not found to distinguish that from a true error case. */
/**
- * Returns the name of a library as declared in the #library directive.
- */
-DART_EXPORT Dart_Handle Dart_LibraryName(Dart_Handle library);
-
-/**
* Returns the url from which a library was loaded.
*/
DART_EXPORT Dart_Handle Dart_LibraryUrl(Dart_Handle library);
-/**
- * Returns a list of the names of all classes and interfaces declared
- * in a library.
- *
- * \return If no error occurs, a list of strings is returned.
- * Otherwise an error handle is returned.
- */
-DART_EXPORT Dart_Handle Dart_LibraryGetClassNames(Dart_Handle library);
-
DART_EXPORT Dart_Handle Dart_LookupLibrary(Dart_Handle url);
/* TODO(turnidge): Consider returning Dart_Null() when the library is
* not found to distinguish that from a true error case. */
@@ -2461,46 +2124,12 @@
Dart_Handle url,
Dart_Handle patch_source);
-/**
- * Sets the callback used to resolve native functions for a library.
- *
- * \param library A library.
- * \param resolver A native entry resolver.
- *
- * \return A valid handle if the native resolver was set successfully.
+
+/*
+ * =====
+ * Peers
+ * =====
*/
-DART_EXPORT Dart_Handle Dart_SetNativeResolver(
- Dart_Handle library,
- Dart_NativeEntryResolver resolver);
-/* TODO(turnidge): Rename to Dart_LibrarySetNativeResolver? */
-
-/* --- Profiling support ---- */
-
-/* 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);
-
-/* Support for generating symbol maps for use by the Linux perf tool. */
-DART_EXPORT void Dart_InitPerfEventsSupport(void* perf_events_file);
-
-/* --- Heap Profiler --- */
-
-/**
- * Generates a heap profile.
- *
- * \param callback A function pointer that will be repeatedly invoked
- * with heap profile data.
- * \param stream A pointer that will be passed to the callback. This
- * is a convenient way to provide an open stream to the callback.
- *
- * \return Success if the heap profile is successful.
- */
-DART_EXPORT Dart_Handle Dart_HeapProfile(Dart_FileWriteCallback callback,
- void* stream);
-
-/* --- Peers --- */
/**
* The peer field is a lazily allocated field intendend for storage of
@@ -2533,124 +2162,4 @@
*/
DART_EXPORT Dart_Handle Dart_SetPeer(Dart_Handle object, void* peer);
-/* --- Message sending/receiving from native code ---- */
-
-/**
- * A Dart_CObject is used for representing Dart objects as native C
- * data outside the Dart heap. These objects are totally detached from
- * the Dart heap. Only a subset of the Dart objects have a
- * representation as a Dart_CObject.
- *
- * The string encoding in the 'value.as_string' is UTF-8.
- *
- * All the different types from dart:typed_data are exposed as type
- * kTypedData. The specific type from dart:typed_data is in the type
- * field of the as_typed_data structure. The length in the
- * as_typed_data structure is always in bytes.
- */
-typedef enum {
- Dart_CObject_kNull = 0,
- Dart_CObject_kBool,
- Dart_CObject_kInt32,
- Dart_CObject_kInt64,
- Dart_CObject_kBigint,
- Dart_CObject_kDouble,
- Dart_CObject_kString,
- Dart_CObject_kArray,
- Dart_CObject_kTypedData,
- Dart_CObject_kExternalTypedData,
- Dart_CObject_kUnsupported,
- Dart_CObject_kNumberOfTypes
-} Dart_CObject_Type;
-
-typedef struct _Dart_CObject {
- Dart_CObject_Type type;
- union {
- bool as_bool;
- int32_t as_int32;
- int64_t as_int64;
- double as_double;
- char* as_string;
- char* as_bigint;
- struct {
- int length;
- struct _Dart_CObject** values;
- } as_array;
- struct {
- Dart_TypedData_Type type;
- int length;
- uint8_t* values;
- } as_typed_data;
- struct {
- Dart_TypedData_Type type;
- int length;
- uint8_t* data;
- void* peer;
- Dart_WeakPersistentHandleFinalizer callback;
- } as_external_typed_data;
- } value;
-} Dart_CObject;
-
-/**
- * Posts a message on some port. The message will contain the
- * Dart_CObject object graph rooted in 'message'.
- *
- * While the message is being sent the state of the graph of
- * Dart_CObject structures rooted in 'message' should not be accessed,
- * as the message generation will make temporary modifications to the
- * data. When the message has been sent the graph will be fully
- * restored.
- *
- * \param port_id The destination port.
- * \param message The message to send.
- *
- * \return True if the message was posted.
- */
-DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message);
-
-/**
- * A native message handler.
- *
- * This handler is associated with a native port by calling
- * Dart_NewNativePort.
- *
- * The message received is decoded into the message structure. The
- * lifetime of the message data is controlled by the caller. All the
- * data references from the message are allocated by the caller and
- * will be reclaimed when returning to it.
- */
-
-typedef void (*Dart_NativeMessageHandler)(Dart_Port dest_port_id,
-Dart_Port reply_port_id,
-Dart_CObject* message);
-
-/**
- * Creates a new native port. When messages are received on this
- * native port, then they will be dispatched to the provided native
- * message handler.
- *
- * \param name The name of this port in debugging messages.
- * \param handler The C handler to run when messages arrive on the port.
- * \param handle_concurrently Is it okay to process requests on this
- * native port concurrently?
- *
- * \return If successful, returns the port id for the native port. In
- * case of error, returns ILLEGAL_PORT.
- */
-DART_EXPORT Dart_Port Dart_NewNativePort(const char* name,
- Dart_NativeMessageHandler handler,
- bool handle_concurrently);
-/* TODO(turnidge): Currently handle_concurrently is ignored. */
-
-/**
- * Closes the native port with the given id.
- *
- * The port must have been allocated by a call to Dart_NewNativePort.
- *
- * \param native_port_id The id of the native port to close.
- *
- * \return Returns true if the port was closed successfully.
- */
-DART_EXPORT bool Dart_CloseNativePort(Dart_Port native_port_id);
-
#endif /* INCLUDE_DART_API_H_ */ /* NOLINT */
diff --git a/runtime/include/dart_mirrors_api.h b/runtime/include/dart_mirrors_api.h
new file mode 100644
index 0000000..60e499a
--- /dev/null
+++ b/runtime/include/dart_mirrors_api.h
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+ * for 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_MIRRORS_API_H_
+#define INCLUDE_DART_MIRRORS_API_H_
+
+#include "include/dart_api.h"
+
+/*
+ * =================================
+ * Classes and Interfaces Reflection
+ * =================================
+ */
+
+/**
+ * Returns the class name for the provided class or interface.
+ */
+DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle clazz);
+
+/**
+ * Returns the library for the provided class or interface.
+ */
+DART_EXPORT Dart_Handle Dart_ClassGetLibrary(Dart_Handle clazz);
+
+/**
+ * Returns the number of interfaces directly implemented by some class
+ * or interface.
+ *
+ * TODO(turnidge): Finish documentation.
+ */
+DART_EXPORT Dart_Handle Dart_ClassGetInterfaceCount(Dart_Handle clazz,
+ intptr_t* count);
+
+/**
+ * Returns the interface at some index in the list of interfaces some
+ * class or inteface.
+ *
+ * TODO(turnidge): Finish documentation.
+ */
+DART_EXPORT Dart_Handle Dart_ClassGetInterfaceAt(Dart_Handle clazz,
+ intptr_t index);
+
+/**
+ * Is this class defined by a typedef?
+ *
+ * Typedef definitions from the main program are represented as a
+ * special kind of class handle. See Dart_ClassGetTypedefReferent.
+ *
+ * TODO(turnidge): Finish documentation.
+ */
+DART_EXPORT bool Dart_ClassIsTypedef(Dart_Handle clazz);
+
+/**
+ * Returns a handle to the type to which a typedef refers.
+ *
+ * It is an error to call this function on a handle for which
+ * Dart_ClassIsTypedef is not true.
+ *
+ * TODO(turnidge): Finish documentation.
+ */
+DART_EXPORT Dart_Handle Dart_ClassGetTypedefReferent(Dart_Handle clazz);
+
+/**
+ * Does this class represent the type of a function?
+ */
+DART_EXPORT bool Dart_ClassIsFunctionType(Dart_Handle clazz);
+
+/**
+ * Returns a function handle representing the signature associated
+ * with a function type.
+ *
+ * The return value is a function handle (See Dart_IsFunction, etc.).
+ *
+ * TODO(turnidge): Finish documentation.
+ */
+DART_EXPORT Dart_Handle Dart_ClassGetFunctionTypeSignature(Dart_Handle clazz);
+
+
+/*
+ * =================================
+ * Function and Variables Reflection
+ * =================================
+ */
+
+/**
+ * Returns a list of the names of all functions or methods declared in
+ * a library or class.
+ *
+ * \param target A library or class.
+ *
+ * \return If no error occurs, a list of strings is returned.
+ * Otherwise an error handle is returned.
+ */
+DART_EXPORT Dart_Handle Dart_GetFunctionNames(Dart_Handle target);
+
+/**
+ * Looks up a function or method declaration by name from a library or
+ * class.
+ *
+ * \param target The library or class containing the function.
+ * \param function_name The name of the function.
+ *
+ * \return If an error is encountered, returns an error handle.
+ * Otherwise returns a function handle if the function is found of
+ * Dart_Null() if the function is not found.
+ */
+DART_EXPORT Dart_Handle Dart_LookupFunction(Dart_Handle target,
+ Dart_Handle function_name);
+
+/**
+ * Returns the name for the provided function or method.
+ *
+ * \return A valid string handle if no error occurs during the
+ * operation.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function);
+
+/**
+ * Returns a handle to the owner of a function.
+ *
+ * The owner of an instance method or a static method is its defining
+ * class. The owner of a top-level function is its defining
+ * library. The owner of the function of a non-implicit closure is the
+ * function of the method or closure that defines the non-implicit
+ * closure.
+ *
+ * \return A valid handle to the owner of the function, or an error
+ * handle if the argument is not a valid handle to a function.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionOwner(Dart_Handle function);
+
+/**
+ * Determines whether a function handle refers to an abstract method.
+ *
+ * \param function A handle to a function or method declaration.
+ * \param is_static Returns whether the handle refers to an abstract method.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionIsAbstract(Dart_Handle function,
+ bool* is_abstract);
+
+/**
+ * Determines whether a function handle referes to a static function
+ * of method.
+ *
+ * For the purposes of the embedding API, a top-level function is
+ * implicitly declared static.
+ *
+ * \param function A handle to a function or method declaration.
+ * \param is_static Returns whether the function or method is declared static.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function,
+ bool* is_static);
+
+/**
+ * Determines whether a function handle referes to a constructor.
+ *
+ * \param function A handle to a function or method declaration.
+ * \param is_static Returns whether the function or method is a constructor.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionIsConstructor(Dart_Handle function,
+ bool* is_constructor);
+/* TODO(turnidge): Document behavior for factory constructors too. */
+
+/**
+ * Determines whether a function or method is a getter.
+ *
+ * \param function A handle to a function or method declaration.
+ * \param is_static Returns whether the function or method is a getter.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionIsGetter(Dart_Handle function,
+ bool* is_getter);
+
+/**
+ * Determines whether a function or method is a setter.
+ *
+ * \param function A handle to a function or method declaration.
+ * \param is_static Returns whether the function or method is a setter.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionIsSetter(Dart_Handle function,
+ bool* is_setter);
+
+/**
+ * Returns the return type of a function.
+ *
+ * \return A valid handle to a type or an error handle if the argument
+ * is not valid.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionReturnType(Dart_Handle function);
+
+/**
+ * Determines the number of required and optional parameters.
+ *
+ * \param function A handle to a function or method declaration.
+ * \param fixed_param_count Returns the number of required parameters.
+ * \param opt_param_count Returns the number of optional parameters.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionParameterCounts(
+ Dart_Handle function,
+ int64_t* fixed_param_count,
+ int64_t* opt_param_count);
+
+/**
+ * Returns a handle to the type of a function parameter.
+ *
+ * \return A valid handle to a type or an error handle if the argument
+ * is not valid.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionParameterType(Dart_Handle function,
+ int parameter_index);
+
+/**
+ * Returns a list of the names of all variables declared in a library
+ * or class.
+ *
+ * \param target A library or class.
+ *
+ * \return If no error occurs, a list of strings is returned.
+ * Otherwise an error handle is returned.
+ */
+DART_EXPORT Dart_Handle Dart_GetVariableNames(Dart_Handle target);
+
+/**
+ * Looks up a variable declaration by name from a library or class.
+ *
+ * \param target The library or class containing the variable.
+ * \param variable_name The name of the variable.
+ *
+ * \return If an error is encountered, returns an error handle.
+ * Otherwise returns a variable handle if the variable is found or
+ * Dart_Null() if the variable is not found.
+ */
+DART_EXPORT Dart_Handle Dart_LookupVariable(Dart_Handle target,
+ Dart_Handle variable_name);
+
+/**
+ * Returns the name for the provided variable.
+ */
+DART_EXPORT Dart_Handle Dart_VariableName(Dart_Handle variable);
+
+/**
+ * Determines whether a variable is declared static.
+ *
+ * For the purposes of the embedding API, a top-level variable is
+ * implicitly declared static.
+ *
+ * \param variable A handle to a variable declaration.
+ * \param is_static Returns whether the variable is declared static.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_VariableIsStatic(Dart_Handle variable,
+ bool* is_static);
+
+/**
+ * Determines whether a variable is declared final.
+ *
+ * \param variable A handle to a variable declaration.
+ * \param is_final Returns whether the variable is declared final.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_VariableIsFinal(Dart_Handle variable,
+ bool* is_final);
+
+/**
+ * Returns the type of a variable.
+ *
+ * \return A valid handle to a type of or an error handle if the
+ * argument is not valid.
+ */
+DART_EXPORT Dart_Handle Dart_VariableType(Dart_Handle function);
+
+/**
+ * Returns a list of the names of all type variables declared in a class.
+ *
+ * The type variables list preserves the original declaration order.
+ *
+ * \param clazz A class.
+ *
+ * \return If no error occurs, a list of strings is returned.
+ * Otherwise an error handle is returned.
+ */
+DART_EXPORT Dart_Handle Dart_GetTypeVariableNames(Dart_Handle clazz);
+
+/**
+ * Looks up a type variable declaration by name from a class.
+ *
+ * \param clazz The class containing the type variable.
+ * \param variable_name The name of the type variable.
+ *
+ * \return If an error is encountered, returns an error handle.
+ * Otherwise returns a type variable handle if the type variable is
+ * found or Dart_Null() if the type variable is not found.
+ */
+DART_EXPORT Dart_Handle Dart_LookupTypeVariable(Dart_Handle clazz,
+ Dart_Handle type_variable_name);
+
+/**
+ * Returns the name for the provided type variable.
+ */
+DART_EXPORT Dart_Handle Dart_TypeVariableName(Dart_Handle type_variable);
+
+/**
+ * Returns the owner of a function.
+ *
+ * The owner of a type variable is its defining class.
+ *
+ * \return A valid handle to the owner of the type variable, or an error
+ * handle if the argument is not a valid handle to a type variable.
+ */
+DART_EXPORT Dart_Handle Dart_TypeVariableOwner(Dart_Handle type_variable);
+
+/**
+ * Returns the upper bound of a type variable.
+ *
+ * The upper bound of a type variable is ...
+ *
+ * \return A valid handle to a type, or an error handle if the
+ * argument is not a valid handle.
+ */
+DART_EXPORT Dart_Handle Dart_TypeVariableUpperBound(Dart_Handle type_variable);
+/* TODO(turnidge): Finish documentation. */
+
+
+/*
+ * ====================
+ * Libraries Reflection
+ * ====================
+ */
+
+/**
+ * Returns the name of a library as declared in the #library directive.
+ */
+DART_EXPORT Dart_Handle Dart_LibraryName(Dart_Handle library);
+
+/**
+ * Returns a list of the names of all classes and interfaces declared
+ * in a library.
+ *
+ * \return If no error occurs, a list of strings is returned.
+ * Otherwise an error handle is returned.
+ */
+DART_EXPORT Dart_Handle Dart_LibraryGetClassNames(Dart_Handle library);
+
+
+/*
+ * ===================
+ * Closures Reflection
+ * ===================
+ */
+
+/**
+ * Retrieves the function of a closure.
+ *
+ * \return A handle to the function of the closure, or an error handle if the
+ * argument is not a closure.
+ */
+DART_EXPORT Dart_Handle Dart_ClosureFunction(Dart_Handle closure);
+
+/*
+ * ===================
+ * Metadata Reflection
+ * ===================
+ */
+
+/**
+ * Get metadata associated with an object.
+ *
+ * \param obj Object for which the metadata is retrieved.
+ *
+ * \return If no error occurs, returns an array of metadata values.
+ * Returns an empty array if there is no metadata for the object.
+ * Returns an error if the evaluation of the metadata expressions fails.
+ *
+ */
+DART_EXPORT Dart_Handle Dart_GetMetadata(Dart_Handle obj);
+
+#endif /* INCLUDE_DART_MIRRORS_API_H_ */ /* NOLINT */
diff --git a/runtime/include/dart_native_api.h b/runtime/include/dart_native_api.h
new file mode 100644
index 0000000..7691d5c
--- /dev/null
+++ b/runtime/include/dart_native_api.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+ * for 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_NATIVE_API_H_
+#define INCLUDE_DART_NATIVE_API_H_
+
+#include "include/dart_api.h"
+
+/*
+ * ==========================================
+ * Message sending/receiving from native code
+ * ==========================================
+ */
+
+/**
+ * A Dart_CObject is used for representing Dart objects as native C
+ * data outside the Dart heap. These objects are totally detached from
+ * the Dart heap. Only a subset of the Dart objects have a
+ * representation as a Dart_CObject.
+ *
+ * The string encoding in the 'value.as_string' is UTF-8.
+ *
+ * All the different types from dart:typed_data are exposed as type
+ * kTypedData. The specific type from dart:typed_data is in the type
+ * field of the as_typed_data structure. The length in the
+ * as_typed_data structure is always in bytes.
+ */
+typedef enum {
+ Dart_CObject_kNull = 0,
+ Dart_CObject_kBool,
+ Dart_CObject_kInt32,
+ Dart_CObject_kInt64,
+ Dart_CObject_kBigint,
+ Dart_CObject_kDouble,
+ Dart_CObject_kString,
+ Dart_CObject_kArray,
+ Dart_CObject_kTypedData,
+ Dart_CObject_kExternalTypedData,
+ Dart_CObject_kUnsupported,
+ Dart_CObject_kNumberOfTypes
+} Dart_CObject_Type;
+
+typedef struct _Dart_CObject {
+ Dart_CObject_Type type;
+ union {
+ bool as_bool;
+ int32_t as_int32;
+ int64_t as_int64;
+ double as_double;
+ char* as_string;
+ char* as_bigint;
+ struct {
+ int length;
+ struct _Dart_CObject** values;
+ } as_array;
+ struct {
+ Dart_TypedData_Type type;
+ int length;
+ uint8_t* values;
+ } as_typed_data;
+ struct {
+ Dart_TypedData_Type type;
+ int length;
+ uint8_t* data;
+ void* peer;
+ Dart_WeakPersistentHandleFinalizer callback;
+ } as_external_typed_data;
+ } value;
+} Dart_CObject;
+
+/**
+ * Posts a message on some port. The message will contain the
+ * Dart_CObject object graph rooted in 'message'.
+ *
+ * While the message is being sent the state of the graph of
+ * Dart_CObject structures rooted in 'message' should not be accessed,
+ * as the message generation will make temporary modifications to the
+ * data. When the message has been sent the graph will be fully
+ * restored.
+ *
+ * \param port_id The destination port.
+ * \param message The message to send.
+ *
+ * \return True if the message was posted.
+ */
+DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message);
+
+/**
+ * A native message handler.
+ *
+ * This handler is associated with a native port by calling
+ * Dart_NewNativePort.
+ *
+ * The message received is decoded into the message structure. The
+ * lifetime of the message data is controlled by the caller. All the
+ * data references from the message are allocated by the caller and
+ * will be reclaimed when returning to it.
+ */
+
+typedef void (*Dart_NativeMessageHandler)(Dart_Port dest_port_id,
+Dart_Port reply_port_id,
+Dart_CObject* message);
+
+/**
+ * Creates a new native port. When messages are received on this
+ * native port, then they will be dispatched to the provided native
+ * message handler.
+ *
+ * \param name The name of this port in debugging messages.
+ * \param handler The C handler to run when messages arrive on the port.
+ * \param handle_concurrently Is it okay to process requests on this
+ * native port concurrently?
+ *
+ * \return If successful, returns the port id for the native port. In
+ * case of error, returns ILLEGAL_PORT.
+ */
+DART_EXPORT Dart_Port Dart_NewNativePort(const char* name,
+ Dart_NativeMessageHandler handler,
+ bool handle_concurrently);
+/* TODO(turnidge): Currently handle_concurrently is ignored. */
+
+/**
+ * Closes the native port with the given id.
+ *
+ * The port must have been allocated by a call to Dart_NewNativePort.
+ *
+ * \param native_port_id The id of the native port to close.
+ *
+ * \return Returns true if the port was closed successfully.
+ */
+DART_EXPORT bool Dart_CloseNativePort(Dart_Port native_port_id);
+
+
+/*
+ * =================
+ * Profiling support
+ * =================
+ */
+
+/* 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);
+
+/* Support for generating symbol maps for use by the Linux perf tool. */
+DART_EXPORT void Dart_InitPerfEventsSupport(void* perf_events_file);
+
+
+/*
+ * =============
+ * Heap Profiler
+ * =============
+ */
+
+/**
+ * Generates a heap profile.
+ *
+ * \param callback A function pointer that will be repeatedly invoked
+ * with heap profile data.
+ * \param stream A pointer that will be passed to the callback. This
+ * is a convenient way to provide an open stream to the callback.
+ *
+ * \return Success if the heap profile is successful.
+ */
+DART_EXPORT Dart_Handle Dart_HeapProfile(Dart_FileWriteCallback callback,
+ void* stream);
+
+
+/*
+ * ==================
+ * Verification Tools
+ * ==================
+ */
+
+/**
+ * Forces all loaded classes and functions to be compiled eagerly in
+ * the current isolate..
+ *
+ * TODO(turnidge): Document.
+ */
+DART_EXPORT Dart_Handle Dart_CompileAll();
+
+/**
+ * Check that all function fingerprints are OK.
+ *
+ */
+DART_EXPORT Dart_Handle Dart_CheckFunctionFingerprints();
+
+#endif /* INCLUDE_DART_NATIVE_API_H_ */ /* NOLINT */
diff --git a/runtime/lib/event_loop_patch.dart b/runtime/lib/event_loop_patch.dart
index c4b25b7..fe04c06 100644
--- a/runtime/lib/event_loop_patch.dart
+++ b/runtime/lib/event_loop_patch.dart
@@ -5,6 +5,6 @@
patch class _AsyncRun {
/* patch */ static void _enqueueImmediate(void callback()) {
// TODO(9001): don't use the Timer to enqueue the immediate callback.
- Timer.run(callback);
+ _createTimer(Duration.ZERO, callback);
}
}
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index a6ea151..7825319 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -4,6 +4,7 @@
#include "include/dart_api.h"
#include "include/dart_debugger_api.h"
+#include "include/dart_mirrors_api.h"
#include "vm/dart_api_impl.h"
#include "vm/bootstrap_natives.h"
#include "vm/dart_entry.h"
diff --git a/runtime/lib/mirrors_impl.dart b/runtime/lib/mirrors_impl.dart
index cb18187..407060b 100644
--- a/runtime/lib/mirrors_impl.dart
+++ b/runtime/lib/mirrors_impl.dart
@@ -253,55 +253,6 @@
native 'LocalObjectMirrorImpl_setField';
}
-// Prints a string as it might appear in dart program text.
-// TODO(turnidge): Consider truncating.
-String _dartEscape(String str) {
- bool isNice(int code) => (code >= 32 && code <= 126);
-
- StringBuffer buf = new StringBuffer();
- for (int i = 0; i < str.length; i++) {
- var input = str[i];
- String output;
- switch (input) {
- case '\\' :
- output = r'\\';
- break;
- case "\'" :
- output = r"\'";
- break;
- case '\n' :
- output = r'\n';
- break;
- case '\r' :
- output = r'\r';
- break;
- case '\f' :
- output = r'\f';
- break;
- case '\b' :
- output = r'\b';
- break;
- case '\t' :
- output = r'\t';
- break;
- case '\v' :
- output = r'\v';
- break;
- default:
- // TODO(lrn): Someone decide if this should combine surrogate pairs.
- int code = input.codeUnitAt(0);
- if (isNice(code)) {
- output = input;
- } else {
- output = '\\u{${code.toRadixString(16)}}';
- }
- break;
- }
- buf.write(output);
- }
- return buf.toString();
-}
-
class _LocalInstanceMirrorImpl extends _LocalObjectMirrorImpl
implements InstanceMirror {
// TODO(ahe): This is a hack, see delegate below.
@@ -340,17 +291,7 @@
return _invokeOnClosure(reflectee, invocation);
}
- String toString() {
- if (_isSimpleValue(_reflectee)) {
- if (_reflectee is String) {
- return "InstanceMirror on <'${_dartEscape(_reflectee)}'>";
- } else {
- return "InstanceMirror on <$_reflectee>";
- }
- } else {
- return "InstanceMirror on instance of '${type.simpleName}'";
- }
- }
+ String toString() => 'InstanceMirror on ${Error.safeToString(_reflectee)}';
}
class _LocalClosureMirrorImpl extends _LocalInstanceMirrorImpl
@@ -402,6 +343,8 @@
static _apply(ref, positionalArguments, async)
native 'LocalClosureMirrorImpl_apply';
+
+ String toString() => "ClosureMirror on '${Error.safeToString(_reflectee)}'";
}
class _LazyTypeMirror {
@@ -568,7 +511,10 @@
'ClassMirror.originalDeclaration is not implemented');
}
- String toString() => "ClassMirror on '$simpleName'";
+ String toString() {
+ String prettyName = isClass ? 'ClassMirror' : 'TypeMirror';
+ return "$prettyName on '${_n(simpleName)}'";
+ }
InstanceMirror newInstance(Symbol constructorName,
List positionalArguments,
@@ -653,7 +599,7 @@
final List<ParameterMirror> parameters;
- String toString() => "FunctionTypeMirror on '$simpleName'";
+ String toString() => "FunctionTypeMirror on '${_n(simpleName)}'";
}
@@ -716,7 +662,7 @@
// reflect() and then make them into a Dart list
List<InstanceMirror> get metadata => _metadata(this).map(reflect).toList();
- String toString() => "TypeVariableMirror on '$simpleName'";
+ String toString() => "TypeVariableMirror on '${_n(simpleName)}'";
}
@@ -762,7 +708,7 @@
return _referent;
}
- String toString() => "TypedefMirror on '$simpleName'";
+ String toString() => "TypedefMirror on '${_n(simpleName)}'";
}
@@ -859,7 +805,7 @@
// reflect() and then make them into a Dart list
List<InstanceMirror> get metadata => _metadata(this).map(reflect).toList();
- String toString() => "LibraryMirror on '$simpleName'";
+ String toString() => "LibraryMirror on '${_n(simpleName)}'";
}
class _LocalMethodMirrorImpl extends _LocalMirrorImpl
@@ -966,7 +912,7 @@
return _metadata(this).map(reflect).toList();
}
- String toString() => "MethodMirror on '$simpleName'";
+ String toString() => "MethodMirror on '${_n(simpleName)}'";
}
class _LocalVariableMirrorImpl extends _LocalMirrorImpl
@@ -1027,7 +973,7 @@
return _metadata(this).map(reflect).toList();
}
- String toString() => "VariableMirror on '$simpleName'";
+ String toString() => "VariableMirror on '${_n(simpleName)}'";
}
class _LocalParameterMirrorImpl extends _LocalVariableMirrorImpl
diff --git a/runtime/lib/timer_patch.dart b/runtime/lib/timer_patch.dart
index 2db2343..87026b1 100644
--- a/runtime/lib/timer_patch.dart
+++ b/runtime/lib/timer_patch.dart
@@ -2,27 +2,26 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-patch class Timer {
- /* patch */ factory Timer(Duration duration, void callback()) {
- if (_TimerFactory._factory == null) {
- throw new UnsupportedError("Timer interface not supported.");
- }
- int milliseconds = duration.inMilliseconds;
- if (milliseconds < 0) milliseconds = 0;
- return _TimerFactory._factory(milliseconds, (_) { callback(); }, false);
+patch Timer _createTimer(Duration duration, void callback()) {
+ if (_TimerFactory._factory == null) {
+ throw new UnsupportedError("Timer interface not supported.");
}
-
- /* patch */ factory Timer.periodic(Duration duration,
- void callback(Timer timer)) {
- if (_TimerFactory._factory == null) {
- throw new UnsupportedError("Timer interface not supported.");
- }
- int milliseconds = duration.inMilliseconds;
- if (milliseconds < 0) milliseconds = 0;
- return _TimerFactory._factory(milliseconds, callback, true);
- }
+ int milliseconds = duration.inMilliseconds;
+ if (milliseconds < 0) milliseconds = 0;
+ return _TimerFactory._factory(milliseconds, (_) { callback(); }, false);
}
+patch Timer _createPeriodicTimer(Duration duration,
+ void callback(Timer timer)) {
+ if (_TimerFactory._factory == null) {
+ throw new UnsupportedError("Timer interface not supported.");
+ }
+ int milliseconds = duration.inMilliseconds;
+ if (milliseconds < 0) milliseconds = 0;
+ return _TimerFactory._factory(milliseconds, callback, true);
+}
+
+
typedef Timer _TimerFactoryClosure(int milliseconds,
void callback(Timer timer),
bool repeating);
diff --git a/runtime/tests/vm/dart/isolate_mirror_local_test.dart b/runtime/tests/vm/dart/isolate_mirror_local_test.dart
index 39f5948..1ec7093 100644
--- a/runtime/tests/vm/dart/isolate_mirror_local_test.dart
+++ b/runtime/tests/vm/dart/isolate_mirror_local_test.dart
@@ -351,7 +351,7 @@
Expect.equals(const Symbol('int'), mirror.type.simpleName);
Expect.isTrue(mirror.hasReflectee);
Expect.equals(1001, mirror.reflectee);
- Expect.equals("InstanceMirror on <1001>", mirror.toString());
+ Expect.equals("InstanceMirror on 1001", mirror.toString());
// Invoke (mirror + mirror).
mirror.invokeAsync(const Symbol('+'), [ mirror ]).then(
@@ -367,7 +367,7 @@
Expect.equals(const Symbol('String'), mirror.type.simpleName);
Expect.isTrue(mirror.hasReflectee);
Expect.equals('This\nis\na\nString', mirror.reflectee);
- Expect.equals("InstanceMirror on <'This\\nis\\na\\nString'>",
+ Expect.equals('InstanceMirror on "This\\nis\\na\\nString"',
mirror.toString());
// Invoke mirror[0].
@@ -384,7 +384,7 @@
Expect.equals(const Symbol('bool'), mirror.type.simpleName);
Expect.isTrue(mirror.hasReflectee);
Expect.equals(true, mirror.reflectee);
- Expect.equals("InstanceMirror on <true>", mirror.toString());
+ Expect.equals("InstanceMirror on true", mirror.toString());
testDone('testBoolInstanceMirror');
}
@@ -393,7 +393,7 @@
Expect.equals(const Symbol('Object'), mirror.type.simpleName);
Expect.isTrue(mirror.hasReflectee);
Expect.equals(null, mirror.reflectee);
- Expect.equals("InstanceMirror on <null>", mirror.toString());
+ Expect.equals("InstanceMirror on null", mirror.toString());
testDone('testNullInstanceMirror');
}
diff --git a/runtime/vm/assembler_ia32.cc b/runtime/vm/assembler_ia32.cc
index 3b35560..55e0f44 100644
--- a/runtime/vm/assembler_ia32.cc
+++ b/runtime/vm/assembler_ia32.cc
@@ -10,6 +10,7 @@
#include "vm/heap.h"
#include "vm/memory_region.h"
#include "vm/runtime_entry.h"
+#include "vm/stack_frame.h"
#include "vm/stub_code.h"
namespace dart {
@@ -2188,15 +2189,16 @@
void Assembler::EnterDartFrame(intptr_t frame_size) {
- const intptr_t offset = CodeSize();
EnterFrame(0);
Label dart_entry;
call(&dart_entry);
Bind(&dart_entry);
- // Adjust saved PC for any intrinsic code that could have been generated
- // before a frame is created.
+ // The runtime system assumes that the code marker address is
+ // kEntryPointToPcMarkerOffset bytes from the entry. If there is any code
+ // generated before entering the frame, the address needs to be adjusted.
+ const intptr_t offset = kEntryPointToPcMarkerOffset - CodeSize();
if (offset != 0) {
- addl(Address(ESP, 0), Immediate(-offset));
+ addl(Address(ESP, 0), Immediate(offset));
}
if (frame_size != 0) {
subl(ESP, Immediate(frame_size));
@@ -2204,6 +2206,29 @@
}
+// On entry to a function compiled for OSR, the caller's frame pointer, the
+// stack locals, and any copied parameters are already in place. The frame
+// pointer is already set up. The PC marker is not correct for the
+// optimized function and there may be extra space for spill slots to
+// allocate.
+void Assembler::EnterOsrFrame(intptr_t extra_size) {
+ Label dart_entry;
+ call(&dart_entry);
+ Bind(&dart_entry);
+ // The runtime system assumes that the code marker address is
+ // kEntryPointToPcMarkerOffset bytes from the entry. Since there is no
+ // code to set up the frame pointer, the address needs to be adjusted.
+ const intptr_t offset = kEntryPointToPcMarkerOffset - CodeSize();
+ if (offset != 0) {
+ addl(Address(ESP, 0), Immediate(offset));
+ }
+ popl(Address(EBP, kPcMarkerSlotFromFp * kWordSize));
+ if (extra_size != 0) {
+ subl(ESP, Immediate(extra_size));
+ }
+}
+
+
void Assembler::EnterStubFrame() {
EnterFrame(0);
pushl(Immediate(0)); // Push 0 in the saved PC area for stub frames.
diff --git a/runtime/vm/assembler_ia32.h b/runtime/vm/assembler_ia32.h
index 7ed62a8..df68c46 100644
--- a/runtime/vm/assembler_ia32.h
+++ b/runtime/vm/assembler_ia32.h
@@ -714,6 +714,11 @@
// .....
void EnterDartFrame(intptr_t frame_size);
+ // Set up a Dart frame for a function compiled for on-stack replacement.
+ // The frame layout is a normal Dart frame, but the frame is partially set
+ // up on entry (it is the frame of the unoptimized code).
+ void EnterOsrFrame(intptr_t extra_size);
+
// Set up a stub frame so that the stack traversal code can easily identify
// a stub frame.
// The stub frame layout is as follows:
diff --git a/runtime/vm/assembler_mips.cc b/runtime/vm/assembler_mips.cc
index 8f43046..f4f3de1 100644
--- a/runtime/vm/assembler_mips.cc
+++ b/runtime/vm/assembler_mips.cc
@@ -346,14 +346,14 @@
// Setup pool pointer for this stub.
Label next;
bal(&next);
- delay_slot()->mov(T0, RA);
+ delay_slot()->mov(TMP1, RA);
const intptr_t object_pool_pc_dist =
Instructions::HeaderSize() - Instructions::object_pool_offset() +
CodeSize();
Bind(&next);
- lw(PP, Address(T0, -object_pool_pc_dist));
+ lw(PP, Address(TMP1, -object_pool_pc_dist));
} else {
addiu(SP, SP, Immediate(-3 * kWordSize));
sw(ZR, Address(SP, 2 * kWordSize)); // PC marker is 0 in stubs.
diff --git a/runtime/vm/assembler_mips.h b/runtime/vm/assembler_mips.h
index 9b22f3e..0b74365 100644
--- a/runtime/vm/assembler_mips.h
+++ b/runtime/vm/assembler_mips.h
@@ -67,6 +67,9 @@
return Utils::IsInt(kImmBits, offset);
}
+ Register base() const { return base_; }
+ int32_t offset() const { return offset_; }
+
private:
Register base_;
int32_t offset_;
@@ -439,6 +442,12 @@
EmitRType(SPECIAL2, rs, rd, rd, 0, CLZ);
}
+ // Convert a 32-bit float in fs to a 64-bit double in dd.
+ void cvtds(DRegister dd, FRegister fs) {
+ FRegister fd = static_cast<FRegister>(dd * 2);
+ EmitFpuRType(COP1, FMT_S, F0, fs, fd, COP1_CVT_D);
+ }
+
// Converts a 32-bit signed int in fs to a double in fd.
void cvtdw(DRegister dd, FRegister fs) {
FRegister fd = static_cast<FRegister>(dd * 2);
@@ -553,10 +562,20 @@
EmitFpuRType(COP1, FMT_D, F0, fs, fd, COP1_MOV);
}
+ // Move if floating point false.
+ void movf(Register rd, Register rs) {
+ EmitRType(SPECIAL, rs, R0, rd, 0, MOVCI);
+ }
+
void movn(Register rd, Register rs, Register rt) {
EmitRType(SPECIAL, rs, rt, rd, 0, MOVN);
}
+ // Move if floating point true.
+ void movt(Register rd, Register rs) {
+ EmitRType(SPECIAL, rs, R1, rd, 0, MOVCI);
+ }
+
void movz(Register rd, Register rs, Register rt) {
EmitRType(SPECIAL, rs, rt, rd, 0, MOVZ);
}
@@ -972,6 +991,20 @@
sra(reg, reg, kSmiTagSize);
}
+ void StoreDToOffset(DRegister reg, Register base, int32_t offset) {
+ FRegister lo = static_cast<FRegister>(reg * 2);
+ FRegister hi = static_cast<FRegister>(reg * 2 + 1);
+ swc1(lo, Address(base, offset));
+ swc1(hi, Address(base, offset + kWordSize));
+ }
+
+ void LoadDFromOffset(DRegister reg, Register base, int32_t offset) {
+ FRegister lo = static_cast<FRegister>(reg * 2);
+ FRegister hi = static_cast<FRegister>(reg * 2 + 1);
+ lwc1(lo, Address(base, offset));
+ lwc1(hi, Address(base, offset + kWordSize));
+ }
+
void ReserveAlignedFrameSpace(intptr_t frame_space);
// Create a frame for calling into runtime that preserves all volatile
diff --git a/runtime/vm/assembler_x64.cc b/runtime/vm/assembler_x64.cc
index 2385749..c1d90db 100644
--- a/runtime/vm/assembler_x64.cc
+++ b/runtime/vm/assembler_x64.cc
@@ -9,6 +9,7 @@
#include "vm/heap.h"
#include "vm/memory_region.h"
#include "vm/runtime_entry.h"
+#include "vm/stack_frame.h"
#include "vm/stub_code.h"
namespace dart {
@@ -2279,15 +2280,16 @@
void Assembler::EnterDartFrame(intptr_t frame_size) {
- const intptr_t offset = CodeSize();
EnterFrame(0);
Label dart_entry;
call(&dart_entry);
Bind(&dart_entry);
- // Adjust saved PC for any intrinsic code that could have been generated
- // before a frame is created.
+ // The runtime system assumes that the code marker address is
+ // kEntryPointToPcMarkerOffset bytes from the entry. If there is any code
+ // generated before entering the frame, the address needs to be adjusted.
+ const intptr_t offset = kEntryPointToPcMarkerOffset - CodeSize();
if (offset != 0) {
- addq(Address(RSP, 0), Immediate(-offset));
+ addq(Address(RSP, 0), Immediate(offset));
}
if (frame_size != 0) {
subq(RSP, Immediate(frame_size));
@@ -2295,6 +2297,29 @@
}
+// On entry to a function compiled for OSR, the caller's frame pointer, the
+// stack locals, and any copied parameters are already in place. The frame
+// pointer is already set up. The PC marker is not correct for the
+// optimized function and there may be extra space for spill slots to
+// allocate.
+void Assembler::EnterOsrFrame(intptr_t extra_size) {
+ Label dart_entry;
+ call(&dart_entry);
+ Bind(&dart_entry);
+ // The runtime system assumes that the code marker address is
+ // kEntryPointToPcMarkerOffset bytes from the entry. Since there is no
+ // code to set up the frame pointer, the address needs to be adjusted.
+ const intptr_t offset = kEntryPointToPcMarkerOffset - CodeSize();
+ if (offset != 0) {
+ addq(Address(RSP, 0), Immediate(offset));
+ }
+ popq(Address(RBP, kPcMarkerSlotFromFp * kWordSize));
+ if (extra_size != 0) {
+ subq(RSP, Immediate(extra_size));
+ }
+}
+
+
void Assembler::EnterStubFrame() {
EnterFrame(0);
pushq(Immediate(0)); // Push 0 in the saved PC area for stub frames.
diff --git a/runtime/vm/assembler_x64.h b/runtime/vm/assembler_x64.h
index c213506..919b1c5 100644
--- a/runtime/vm/assembler_x64.h
+++ b/runtime/vm/assembler_x64.h
@@ -735,6 +735,11 @@
// .....
void EnterDartFrame(intptr_t frame_size);
+ // Set up a Dart frame for a function compiled for on-stack replacement.
+ // The frame layout is a normal Dart frame, but the frame is partially set
+ // up on entry (it is the frame of the unoptimized code).
+ void EnterOsrFrame(intptr_t extra_size);
+
// Set up a stub frame so that the stack traversal code can easily identify
// a stub frame.
// The stub frame layout is as follows:
diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc
index 68e167f..6eec38d 100644
--- a/runtime/vm/code_generator.cc
+++ b/runtime/vm/code_generator.cc
@@ -54,6 +54,13 @@
DEFINE_FLAG(int, max_subtype_cache_entries, 100,
"Maximum number of subtype cache entries (number of checks cached).");
+#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
+DEFINE_FLAG(bool, use_osr, true, "Use on-stack replacement.");
+#else
+DEFINE_FLAG(bool, use_osr, false, "Use on-stack replacement.");
+#endif
+DEFINE_FLAG(bool, trace_osr, false, "Trace attempts at on-stack replacement.");
+
DEFINE_RUNTIME_ENTRY(TraceFunctionEntry, 1) {
ASSERT(arguments.ArgCount() ==
@@ -1265,6 +1272,39 @@
(*callback)();
}
}
+
+ if (FLAG_use_osr && (interrupt_bits == 0)) {
+ DartFrameIterator iterator;
+ StackFrame* frame = iterator.NextFrame();
+ const Function& function = Function::Handle(frame->LookupDartFunction());
+ ASSERT(!function.IsNull());
+ if (!function.is_optimizable()) return;
+ intptr_t osr_id =
+ Code::Handle(function.unoptimized_code()).GetDeoptIdForOsr(frame->pc());
+ if (FLAG_trace_osr) {
+ OS::Print("Attempting OSR for %s at id=%"Pd"\n",
+ function.ToFullyQualifiedCString(),
+ osr_id);
+ }
+
+ const Code& original_code = Code::Handle(function.CurrentCode());
+ const Error& error =
+ Error::Handle(Compiler::CompileOptimizedFunction(function, osr_id));
+ if (!error.IsNull()) Exceptions::PropagateError(error);
+
+ const Code& optimized_code = Code::Handle(function.CurrentCode());
+ // The current code will not be changed in the case that the compiler
+ // bailed out during OSR compilation.
+ if (optimized_code.raw() != original_code.raw()) {
+ // The OSR code does not work for calling the function, so restore the
+ // unoptimized code. Patch the stack frame to return into the OSR
+ // code.
+ uword optimized_entry =
+ Instructions::Handle(optimized_code.instructions()).EntryPoint();
+ function.SetCode(original_code);
+ frame->set_pc(optimized_entry);
+ }
+ }
}
@@ -1328,9 +1368,8 @@
}
const Code& optimized_code = Code::Handle(function.CurrentCode());
ASSERT(!optimized_code.IsNull());
- // Set usage counter for reoptimization.
- function.set_usage_counter(
- function.usage_counter() - FLAG_reoptimization_counter_threshold);
+ // Reset usage counter for reoptimization.
+ function.set_usage_counter(0);
} else {
if (FLAG_trace_failed_optimization_attempts) {
OS::PrintErr("Not Optimizable: %s\n", function.ToFullyQualifiedCString());
diff --git a/runtime/vm/code_patcher_mips.cc b/runtime/vm/code_patcher_mips.cc
index 96088fd..5cd9108 100644
--- a/runtime/vm/code_patcher_mips.cc
+++ b/runtime/vm/code_patcher_mips.cc
@@ -47,7 +47,9 @@
void CodePatcher::InsertCallAt(uword start, uword target) {
- UNIMPLEMENTED();
+ // The inserted call should not overlap the lazy deopt jump code.
+ ASSERT(start + CallPattern::kFixedLengthInBytes <= target);
+ CallPattern::InsertAt(start, target);
}
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index a0f5306..0fba07c 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -12,7 +12,6 @@
#include "vm/dart_entry.h"
#include "vm/debugger.h"
#include "vm/deopt_instructions.h"
-#include "vm/disassembler.h"
#include "vm/exceptions.h"
#include "vm/flags.h"
#include "vm/flow_graph.h"
@@ -241,7 +240,8 @@
// Return false if bailed out.
static bool CompileParsedFunctionHelper(ParsedFunction* parsed_function,
- bool optimized) {
+ bool optimized,
+ intptr_t osr_id) {
const Function& function = parsed_function->function();
if (optimized && !function.is_optimizable()) {
return false;
@@ -281,13 +281,18 @@
// Build the flow graph.
FlowGraphBuilder builder(parsed_function,
ic_data_array,
- NULL); // NULL = not inlining.
+ NULL, // NULL = not inlining.
+ osr_id);
flow_graph = builder.BuildGraph();
}
if (FLAG_print_flow_graph ||
(optimized && FLAG_print_flow_graph_optimized)) {
- FlowGraphPrinter::PrintGraph("Before Optimizations", flow_graph);
+ if (osr_id == Isolate::kNoDeoptId) {
+ FlowGraphPrinter::PrintGraph("Before Optimizations", flow_graph);
+ } else {
+ FlowGraphPrinter::PrintGraph("For OSR", flow_graph);
+ }
}
if (optimized) {
@@ -508,12 +513,14 @@
graph_compiler.FinalizeStaticCallTargetsTable(code);
if (optimized) {
- CodePatcher::PatchEntry(Code::Handle(function.CurrentCode()));
- function.SetCode(code);
- if (FLAG_trace_compiler) {
- OS::Print("--> patching entry %#"Px"\n",
- Code::Handle(function.unoptimized_code()).EntryPoint());
+ if (osr_id == Isolate::kNoDeoptId) {
+ CodePatcher::PatchEntry(Code::Handle(function.CurrentCode()));
+ if (FLAG_trace_compiler) {
+ OS::Print("--> patching entry %#"Px"\n",
+ Code::Handle(function.unoptimized_code()).EntryPoint());
+ }
}
+ function.SetCode(code);
for (intptr_t i = 0; i < guarded_fields.length(); i++) {
const Field& field = *guarded_fields[i];
@@ -551,12 +558,7 @@
optimized ? "optimized " : "",
function_fullname);
const Code& code = Code::Handle(function.CurrentCode());
- const Instructions& instructions =
- Instructions::Handle(code.instructions());
- uword start = instructions.EntryPoint();
- Disassembler::Disassemble(start,
- start + instructions.size(),
- code.comments());
+ code.Disassemble();
OS::Print("}\n");
OS::Print("Pointer offsets for function: {\n");
@@ -576,6 +578,7 @@
PcDescriptors::Handle(code.pc_descriptors());
OS::Print("%s}\n", descriptors.ToCString());
+ uword start = Instructions::Handle(code.instructions()).EntryPoint();
const Array& deopt_table = Array::Handle(code.deopt_info_array());
intptr_t deopt_table_length = DeoptTable::GetLength(deopt_table);
if (deopt_table_length > 0) {
@@ -675,7 +678,8 @@
static RawError* CompileFunctionHelper(const Function& function,
- bool optimized) {
+ bool optimized,
+ intptr_t osr_id) {
Isolate* isolate = Isolate::Current();
StackZone zone(isolate);
LongJump* base = isolate->long_jump_base();
@@ -694,7 +698,8 @@
ParsedFunction* parsed_function = new ParsedFunction(
Function::ZoneHandle(function.raw()));
if (FLAG_trace_compiler) {
- OS::Print("Compiling %sfunction: '%s' @ token %"Pd", size %"Pd"\n",
+ OS::Print("Compiling %s%sfunction: '%s' @ token %"Pd", size %"Pd"\n",
+ (osr_id == Isolate::kNoDeoptId ? "" : "osr "),
(optimized ? "optimized " : ""),
function.ToFullyQualifiedCString(),
function.token_pos(),
@@ -707,7 +712,7 @@
}
const bool success =
- CompileParsedFunctionHelper(parsed_function, optimized);
+ CompileParsedFunctionHelper(parsed_function, optimized, osr_id);
if (optimized && !success) {
// Optimizer bailed out. Disable optimizations and to never try again.
if (FLAG_trace_compiler) {
@@ -759,12 +764,13 @@
RawError* Compiler::CompileFunction(const Function& function) {
- return CompileFunctionHelper(function, false); // Non-optimized.
+ return CompileFunctionHelper(function, false, Isolate::kNoDeoptId);
}
-RawError* Compiler::CompileOptimizedFunction(const Function& function) {
- return CompileFunctionHelper(function, true); // Optimized.
+RawError* Compiler::CompileOptimizedFunction(const Function& function,
+ intptr_t osr_id) {
+ return CompileFunctionHelper(function, true, osr_id);
}
@@ -776,7 +782,7 @@
isolate->set_long_jump_base(&jump);
if (setjmp(*jump.Set()) == 0) {
// Non-optimized code generator.
- CompileParsedFunctionHelper(parsed_function, false);
+ CompileParsedFunctionHelper(parsed_function, false, Isolate::kNoDeoptId);
if (FLAG_disassemble) {
DisassembleCode(parsed_function->function(), false);
}
@@ -863,7 +869,7 @@
parsed_function->AllocateVariables();
// Non-optimized code generator.
- CompileParsedFunctionHelper(parsed_function, false);
+ CompileParsedFunctionHelper(parsed_function, false, Isolate::kNoDeoptId);
const Object& result = Object::Handle(
DartEntry::InvokeFunction(func, Object::empty_array()));
diff --git a/runtime/vm/compiler.h b/runtime/vm/compiler.h
index 352ac4f..ab49c80 100644
--- a/runtime/vm/compiler.h
+++ b/runtime/vm/compiler.h
@@ -44,7 +44,9 @@
// Generates optimized code for function.
//
// Returns Error::null() if there is no compilation error.
- static RawError* CompileOptimizedFunction(const Function& function);
+ static RawError* CompileOptimizedFunction(
+ const Function& function,
+ intptr_t osr_id = Isolate::kNoDeoptId);
// Generates code for given parsed function (without parsing it again) and
// sets its code field.
diff --git a/runtime/vm/constants_mips.h b/runtime/vm/constants_mips.h
index e016f7b..b1f910a 100644
--- a/runtime/vm/constants_mips.h
+++ b/runtime/vm/constants_mips.h
@@ -156,9 +156,13 @@
kNoDRegister = -1,
};
+const DRegister DTMP = D9;
+const FRegister STMP1 = F18;
+const FRegister STMP2 = F19;
+
// Architecture independent aliases.
typedef DRegister FpuRegister;
-const FpuRegister FpuTMP = D0;
+const FpuRegister FpuTMP = DTMP;
const int kNumberOfFpuRegisters = kNumberOfDRegisters;
const FpuRegister kNoFpuRegister = kNoDRegister;
@@ -172,13 +176,6 @@
const Register SPREG = SP; // Stack pointer register.
const Register FPREG = FP; // Frame pointer register.
-// NULLREG holds reinterpret_cast<intptr_t>(Object::null()).
-// TODO(zra): Is it worthwhile to devote a register to this? Investigate
-// performance effects when we are running on real hardware. Same with
-// CMPRES. Try moving CTX and PP to T8 and T9 and shifting kLastCpuRegister
-// down to S7.
-const Register NULLREG = T8;
-
// The code that generates a comparison can be far away from the code that
// generates the branch that uses the result of that comparison. In this case,
// CMPRES is used for the result of the comparison.
diff --git a/runtime/vm/custom_isolate_test.cc b/runtime/vm/custom_isolate_test.cc
index 70fb4c7..a32c36e9 100644
--- a/runtime/vm/custom_isolate_test.cc
+++ b/runtime/vm/custom_isolate_test.cc
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "include/dart_api.h"
+#include "include/dart_native_api.h"
#include "vm/unit_test.h"
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 323e4f0..176407c 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
#include "include/dart_api.h"
+#include "include/dart_mirrors_api.h"
+#include "include/dart_native_api.h"
#include "platform/assert.h"
#include "vm/bigint_operations.h"
@@ -18,8 +20,8 @@
#include "vm/flags.h"
#include "vm/growable_array.h"
#include "vm/message.h"
+#include "vm/message_handler.h"
#include "vm/native_entry.h"
-#include "vm/native_message_handler.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/port.h"
@@ -51,37 +53,23 @@
}
-#define RETURN_TYPE_ERROR(isolate, dart_handle, type) \
- do { \
- const Object& tmp = \
- Object::Handle(isolate, Api::UnwrapHandle((dart_handle))); \
- if (tmp.IsNull()) { \
- return Api::NewError("%s expects argument '%s' to be non-null.", \
- CURRENT_FUNC, #dart_handle); \
- } else if (tmp.IsError()) { \
- return dart_handle; \
- } else { \
- return Api::NewError("%s expects argument '%s' to be of type %s.", \
- CURRENT_FUNC, #dart_handle, #type); \
- } \
- } while (0)
-
-
-#define RETURN_NULL_ERROR(parameter) \
- return Api::NewError("%s expects argument '%s' to be non-null.", \
- CURRENT_FUNC, #parameter);
-
-
-#define CHECK_LENGTH(length, max_elements) \
- do { \
- intptr_t len = (length); \
- intptr_t max = (max_elements); \
- if (len < 0 || len > max) { \
- return Api::NewError( \
- "%s expects argument '%s' to be in the range [0..%"Pd"].", \
- CURRENT_FUNC, #length, max); \
- } \
- } while (0)
+static RawInstance* GetListInstance(Isolate* isolate, const Object& obj) {
+ if (obj.IsInstance()) {
+ const Instance& instance = Instance::Cast(obj);
+ const Class& obj_class = Class::Handle(isolate, obj.clazz());
+ const Class& list_class =
+ Class::Handle(isolate, isolate->object_store()->list_class());
+ Error& malformed_type_error = Error::Handle(isolate);
+ if (obj_class.IsSubtypeOf(TypeArguments::Handle(isolate),
+ list_class,
+ TypeArguments::Handle(isolate),
+ &malformed_type_error)) {
+ ASSERT(malformed_type_error.IsNull()); // Type is a raw List.
+ return instance.raw();
+ }
+ }
+ return Instance::null();
+}
Dart_Handle Api::NewHandle(Isolate* isolate, RawObject* raw) {
@@ -92,6 +80,7 @@
return reinterpret_cast<Dart_Handle>(ref);
}
+
RawObject* Api::UnwrapHandle(Dart_Handle object) {
#if defined(DEBUG)
Isolate* isolate = Isolate::Current();
@@ -108,6 +97,7 @@
return (reinterpret_cast<LocalHandle*>(object))->raw();
}
+
#define DEFINE_UNWRAP(type) \
const type& Api::Unwrap##type##Handle(Isolate* iso, \
Dart_Handle dart_handle) { \
@@ -267,36 +257,8 @@
}
-// When we want to return a handle to a type to the user, we handle
-// class-types differently than some other types.
-static Dart_Handle TypeToHandle(Isolate* isolate,
- const char* function_name,
- const AbstractType& type) {
- if (type.IsMalformed()) {
- const Error& error = Error::Handle(type.malformed_error());
- return Api::NewError("%s: malformed type encountered: %s.",
- function_name, error.ToErrorCString());
- } else if (type.HasResolvedTypeClass()) {
- const Class& cls = Class::Handle(isolate, type.type_class());
-#if defined(DEBUG)
- const Library& lib = Library::Handle(cls.library());
- if (lib.IsNull()) {
- ASSERT(cls.IsDynamicClass() || cls.IsVoidClass());
- }
-#endif
- return Api::NewHandle(isolate, cls.raw());
- } else if (type.IsTypeParameter()) {
- return Api::NewHandle(isolate, type.raw());
- } else {
- return Api::NewError("%s: unexpected type '%s' encountered.",
- function_name, type.ToCString());
- }
-}
-
-
// --- Handles ---
-
DART_EXPORT bool Dart_IsError(Dart_Handle handle) {
return RawObject::IsErrorClassId(Api::ClassId(handle));
}
@@ -674,7 +636,6 @@
// --- Garbage Collection Callbacks --
-
DART_EXPORT Dart_Handle Dart_AddGcPrologueCallback(
Dart_GcPrologueCallback callback) {
Isolate* isolate = Isolate::Current();
@@ -737,17 +698,6 @@
}
-DART_EXPORT Dart_Handle Dart_HeapProfile(Dart_FileWriteCallback callback,
- void* stream) {
- Isolate* isolate = Isolate::Current();
- CHECK_ISOLATE(isolate);
- if (callback == NULL) {
- RETURN_NULL_ERROR(callback);
- }
- isolate->heap()->Profile(callback, stream);
- return Api::Success();
-}
-
// --- Initialization and Globals ---
DART_EXPORT const char* Dart_VersionString() {
@@ -787,7 +737,6 @@
// --- Isolates ---
-
static char* BuildIsolateName(const char* script_uri,
const char* main) {
if (script_uri == NULL) {
@@ -1049,34 +998,6 @@
}
-DART_EXPORT bool Dart_PostIntArray(Dart_Port port_id,
- intptr_t len,
- intptr_t* data) {
- uint8_t* buffer = NULL;
- ApiMessageWriter writer(&buffer, &allocator);
- writer.WriteMessage(len, data);
-
- // Post the message at the given port.
- return PortMap::PostMessage(new Message(
- port_id, Message::kIllegalPort, buffer, writer.BytesWritten(),
- Message::kNormalPriority));
-}
-
-
-DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message) {
- uint8_t* buffer = NULL;
- ApiMessageWriter writer(&buffer, allocator);
- bool success = writer.WriteCMessage(message);
-
- if (!success) return success;
-
- // Post the message at the given port.
- return PortMap::PostMessage(new Message(
- port_id, Message::kIllegalPort, buffer, writer.BytesWritten(),
- Message::kNormalPriority));
-}
-
-
DART_EXPORT bool Dart_Post(Dart_Port port_id, Dart_Handle handle) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
@@ -1090,38 +1011,6 @@
}
-DART_EXPORT Dart_Port Dart_NewNativePort(const char* name,
- Dart_NativeMessageHandler handler,
- bool handle_concurrently) {
- if (name == NULL) {
- name = "<UnnamedNativePort>";
- }
- if (handler == NULL) {
- OS::PrintErr("%s expects argument 'handler' to be non-null.\n",
- CURRENT_FUNC);
- return ILLEGAL_PORT;
- }
- // Start the native port without a current isolate.
- IsolateSaver saver(Isolate::Current());
- Isolate::SetCurrent(NULL);
-
- NativeMessageHandler* nmh = new NativeMessageHandler(name, handler);
- Dart_Port port_id = PortMap::CreatePort(nmh);
- nmh->Run(Dart::thread_pool(), NULL, NULL, 0);
- return port_id;
-}
-
-
-DART_EXPORT bool Dart_CloseNativePort(Dart_Port native_port_id) {
- // Close the native port without a current isolate.
- IsolateSaver saver(Isolate::Current());
- Isolate::SetCurrent(NULL);
-
- // TODO(turnidge): Check that the port is native before trying to close.
- return PortMap::ClosePort(native_port_id);
-}
-
-
DART_EXPORT Dart_Handle Dart_NewSendPort(Dart_Port port_id) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
@@ -1164,8 +1053,8 @@
return isolate->main_port();
}
-// --- Scopes ----
+// --- Scopes ----
DART_EXPORT void Dart_EnterScope() {
Isolate* isolate = Isolate::Current();
@@ -1209,7 +1098,6 @@
// --- Objects ----
-
DART_EXPORT Dart_Handle Dart_Null() {
Isolate* isolate = Isolate::Current();
CHECK_ISOLATE_SCOPE(isolate);
@@ -1286,9 +1174,6 @@
}
-// --- Instances ----
-
-
DART_EXPORT bool Dart_IsInstance(Dart_Handle object) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
@@ -1297,6 +1182,104 @@
}
+DART_EXPORT bool Dart_IsNumber(Dart_Handle object) {
+ return RawObject::IsNumberClassId(Api::ClassId(object));
+}
+
+
+DART_EXPORT bool Dart_IsInteger(Dart_Handle object) {
+ return RawObject::IsIntegerClassId(Api::ClassId(object));
+}
+
+
+DART_EXPORT bool Dart_IsDouble(Dart_Handle object) {
+ return Api::ClassId(object) == kDoubleCid;
+}
+
+
+DART_EXPORT bool Dart_IsBoolean(Dart_Handle object) {
+ return Api::ClassId(object) == kBoolCid;
+}
+
+
+DART_EXPORT bool Dart_IsString(Dart_Handle object) {
+ return RawObject::IsStringClassId(Api::ClassId(object));
+}
+
+
+DART_EXPORT bool Dart_IsStringLatin1(Dart_Handle object) {
+ return RawObject::IsOneByteStringClassId(Api::ClassId(object));
+}
+
+
+DART_EXPORT bool Dart_IsExternalString(Dart_Handle object) {
+ return RawObject::IsExternalStringClassId(Api::ClassId(object));
+}
+
+
+DART_EXPORT bool Dart_IsList(Dart_Handle object) {
+ if (RawObject::IsBuiltinListClassId(Api::ClassId(object))) {
+ return true;
+ }
+
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(object));
+ return GetListInstance(isolate, obj) != Instance::null();
+}
+
+
+DART_EXPORT bool Dart_IsLibrary(Dart_Handle object) {
+ return Api::ClassId(object) == kLibraryCid;
+}
+
+
+DART_EXPORT bool Dart_IsClass(Dart_Handle handle) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(handle));
+ return obj.IsClass();
+}
+
+
+DART_EXPORT bool Dart_IsAbstractClass(Dart_Handle handle) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(handle));
+ if (obj.IsClass()) {
+ return Class::Cast(obj).is_abstract();
+ }
+ return false;
+}
+
+
+DART_EXPORT bool Dart_IsFunction(Dart_Handle handle) {
+ return Api::ClassId(handle) == kFunctionCid;
+}
+
+
+DART_EXPORT bool Dart_IsVariable(Dart_Handle handle) {
+ return Api::ClassId(handle) == kFieldCid;
+}
+
+
+DART_EXPORT bool Dart_IsTypeVariable(Dart_Handle handle) {
+ return Api::ClassId(handle) == kTypeParameterCid;
+}
+
+
+DART_EXPORT bool Dart_IsClosure(Dart_Handle object) {
+ // We can't use a fast class index check here because there are many
+ // different signature classes for closures.
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Instance& closure_obj = Api::UnwrapInstanceHandle(isolate, object);
+ return (!closure_obj.IsNull() && closure_obj.IsClosure());
+}
+
+
+// --- Instances ----
+
// TODO(turnidge): Technically, null has a class. Should we allow it?
DART_EXPORT Dart_Handle Dart_InstanceGetClass(Dart_Handle instance) {
Isolate* isolate = Isolate::Current();
@@ -1309,21 +1292,7 @@
}
-// --- Numbers ----
-
-
-DART_EXPORT bool Dart_IsNumber(Dart_Handle object) {
- return RawObject::IsNumberClassId(Api::ClassId(object));
-}
-
-
-// --- Integers ----
-
-
-DART_EXPORT bool Dart_IsInteger(Dart_Handle object) {
- return RawObject::IsIntegerClassId(Api::ClassId(object));
-}
-
+// --- Numbers, Integers and Doubles ----
DART_EXPORT Dart_Handle Dart_IntegerFitsIntoInt64(Dart_Handle integer,
bool* fits) {
@@ -1486,9 +1455,29 @@
}
-// --- Booleans ----
+DART_EXPORT Dart_Handle Dart_NewDouble(double value) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ CHECK_CALLBACK_STATE(isolate);
+ return Api::NewHandle(isolate, Double::New(value));
+}
+DART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj,
+ double* value) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Double& obj = Api::UnwrapDoubleHandle(isolate, double_obj);
+ if (obj.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, double_obj, Double);
+ }
+ *value = obj.value();
+ return Api::Success();
+}
+
+
+// --- Booleans ----
+
DART_EXPORT Dart_Handle Dart_True() {
Isolate* isolate = Isolate::Current();
CHECK_ISOLATE_SCOPE(isolate);
@@ -1503,11 +1492,6 @@
}
-DART_EXPORT bool Dart_IsBoolean(Dart_Handle object) {
- return Api::ClassId(object) == kBoolCid;
-}
-
-
DART_EXPORT Dart_Handle Dart_NewBoolean(bool value) {
Isolate* isolate = Isolate::Current();
CHECK_ISOLATE_SCOPE(isolate);
@@ -1528,48 +1512,9 @@
}
-// --- Doubles ---
-
-
-DART_EXPORT bool Dart_IsDouble(Dart_Handle object) {
- return Api::ClassId(object) == kDoubleCid;
-}
-
-
-DART_EXPORT Dart_Handle Dart_NewDouble(double value) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- CHECK_CALLBACK_STATE(isolate);
- return Api::NewHandle(isolate, Double::New(value));
-}
-
-
-DART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj,
- double* value) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Double& obj = Api::UnwrapDoubleHandle(isolate, double_obj);
- if (obj.IsNull()) {
- RETURN_TYPE_ERROR(isolate, double_obj, Double);
- }
- *value = obj.value();
- return Api::Success();
-}
-
-
// --- Strings ---
-DART_EXPORT bool Dart_IsString(Dart_Handle object) {
- return RawObject::IsStringClassId(Api::ClassId(object));
-}
-
-
-DART_EXPORT bool Dart_IsStringLatin1(Dart_Handle object) {
- return RawObject::IsOneByteStringClassId(Api::ClassId(object));
-}
-
-
DART_EXPORT Dart_Handle Dart_StringLength(Dart_Handle str, intptr_t* len) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
@@ -1636,11 +1581,6 @@
}
-DART_EXPORT bool Dart_IsExternalString(Dart_Handle object) {
- return RawObject::IsExternalStringClassId(Api::ClassId(object));
-}
-
-
DART_EXPORT Dart_Handle Dart_ExternalStringGetPeer(Dart_Handle object,
void** peer) {
if (peer == NULL) {
@@ -1863,38 +1803,6 @@
// --- Lists ---
-
-static RawInstance* GetListInstance(Isolate* isolate, const Object& obj) {
- if (obj.IsInstance()) {
- const Instance& instance = Instance::Cast(obj);
- const Class& obj_class = Class::Handle(isolate, obj.clazz());
- const Class& list_class =
- Class::Handle(isolate, isolate->object_store()->list_class());
- Error& malformed_type_error = Error::Handle(isolate);
- if (obj_class.IsSubtypeOf(TypeArguments::Handle(isolate),
- list_class,
- TypeArguments::Handle(isolate),
- &malformed_type_error)) {
- ASSERT(malformed_type_error.IsNull()); // Type is a raw List.
- return instance.raw();
- }
- }
- return Instance::null();
-}
-
-
-DART_EXPORT bool Dart_IsList(Dart_Handle object) {
- if (RawObject::IsBuiltinListClassId(Api::ClassId(object))) {
- return true;
- }
-
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(object));
- return GetListInstance(isolate, obj) != Instance::null();
-}
-
-
DART_EXPORT Dart_Handle Dart_NewList(intptr_t length) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
@@ -2358,7 +2266,6 @@
// --- Typed Data ---
-
// Helper method to get the type of a TypedData object.
static Dart_TypedData_Type GetType(intptr_t class_id) {
Dart_TypedData_Type type;
@@ -2750,840 +2657,7 @@
}
-// --- Closures ---
-
-
-DART_EXPORT bool Dart_IsClosure(Dart_Handle object) {
- // We can't use a fast class index check here because there are many
- // different signature classes for closures.
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Instance& closure_obj = Api::UnwrapInstanceHandle(isolate, object);
- return (!closure_obj.IsNull() && closure_obj.IsClosure());
-}
-
-
-DART_EXPORT Dart_Handle Dart_ClosureFunction(Dart_Handle closure) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Instance& closure_obj = Api::UnwrapInstanceHandle(isolate, closure);
- if (closure_obj.IsNull() || !closure_obj.IsClosure()) {
- RETURN_TYPE_ERROR(isolate, closure, Instance);
- }
-
- ASSERT(ClassFinalizer::AllClassesFinalized());
-
- RawFunction* rf = Closure::function(closure_obj);
- return Api::NewHandle(isolate, rf);
-}
-
-
-DART_EXPORT Dart_Handle Dart_InvokeClosure(Dart_Handle closure,
- int number_of_arguments,
- Dart_Handle* arguments) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- CHECK_CALLBACK_STATE(isolate);
- const Instance& closure_obj = Api::UnwrapInstanceHandle(isolate, closure);
- if (closure_obj.IsNull() || !closure_obj.IsCallable(NULL, NULL)) {
- RETURN_TYPE_ERROR(isolate, closure, Instance);
- }
- if (number_of_arguments < 0) {
- return Api::NewError(
- "%s expects argument 'number_of_arguments' to be non-negative.",
- CURRENT_FUNC);
- }
- ASSERT(ClassFinalizer::AllClassesFinalized());
-
- // Set up arguments to include the closure as the first argument.
- const Array& args = Array::Handle(isolate,
- Array::New(number_of_arguments + 1));
- Object& obj = Object::Handle(isolate);
- args.SetAt(0, closure_obj);
- for (int i = 0; i < number_of_arguments; i++) {
- obj = Api::UnwrapHandle(arguments[i]);
- if (!obj.IsNull() && !obj.IsInstance()) {
- RETURN_TYPE_ERROR(isolate, arguments[i], Instance);
- }
- args.SetAt(i + 1, obj);
- }
- // Now try to invoke the closure.
- return Api::NewHandle(isolate, DartEntry::InvokeClosure(args));
-}
-
-
-// --- Classes ---
-
-
-DART_EXPORT bool Dart_IsClass(Dart_Handle handle) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(handle));
- return obj.IsClass();
-}
-
-
-DART_EXPORT bool Dart_IsAbstractClass(Dart_Handle handle) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(handle));
- if (obj.IsClass()) {
- return Class::Cast(obj).is_abstract();
- }
- return false;
-}
-
-
-DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle clazz) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
- if (cls.IsNull()) {
- RETURN_TYPE_ERROR(isolate, clazz, Class);
- }
- return Api::NewHandle(isolate, cls.UserVisibleName());
-}
-
-
-DART_EXPORT Dart_Handle Dart_ClassGetLibrary(Dart_Handle clazz) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
- if (cls.IsNull()) {
- RETURN_TYPE_ERROR(isolate, clazz, Class);
- }
-
-#if defined(DEBUG)
- const Library& lib = Library::Handle(cls.library());
- if (lib.IsNull()) {
- // ASSERT(cls.IsDynamicClass() || cls.IsVoidClass());
- if (!cls.IsDynamicClass() && !cls.IsVoidClass()) {
- fprintf(stderr, "NO LIBRARY: %s\n", cls.ToCString());
- }
- }
-#endif
-
- return Api::NewHandle(isolate, cls.library());
-}
-
-
-DART_EXPORT Dart_Handle Dart_ClassGetInterfaceCount(Dart_Handle clazz,
- intptr_t* count) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
- if (cls.IsNull()) {
- RETURN_TYPE_ERROR(isolate, clazz, Class);
- }
-
- const Array& interface_types = Array::Handle(isolate, cls.interfaces());
- if (interface_types.IsNull()) {
- *count = 0;
- } else {
- *count = interface_types.Length();
- }
- return Api::Success();
-}
-
-
-DART_EXPORT Dart_Handle Dart_ClassGetInterfaceAt(Dart_Handle clazz,
- intptr_t index) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
- if (cls.IsNull()) {
- RETURN_TYPE_ERROR(isolate, clazz, Class);
- }
-
- // Finalize all classes.
- Dart_Handle state = Api::CheckIsolateState(isolate);
- if (::Dart_IsError(state)) {
- return state;
- }
-
- const Array& interface_types = Array::Handle(isolate, cls.interfaces());
- if (index < 0 || index >= interface_types.Length()) {
- return Api::NewError("%s: argument 'index' out of bounds.", CURRENT_FUNC);
- }
- Type& interface_type = Type::Handle(isolate);
- interface_type ^= interface_types.At(index);
- if (interface_type.HasResolvedTypeClass()) {
- return Api::NewHandle(isolate, interface_type.type_class());
- }
- const String& type_name =
- String::Handle(isolate, interface_type.TypeClassName());
- return Api::NewError("%s: internal error: found unresolved type class '%s'.",
- CURRENT_FUNC, type_name.ToCString());
-}
-
-
-DART_EXPORT bool Dart_ClassIsTypedef(Dart_Handle clazz) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
- if (cls.IsNull()) {
- RETURN_TYPE_ERROR(isolate, clazz, Class);
- }
- // For now we represent typedefs as non-canonical signature classes.
- // I anticipate this may change if we make typedefs more general.
- return cls.IsSignatureClass() && !cls.IsCanonicalSignatureClass();
-}
-
-
-DART_EXPORT Dart_Handle Dart_ClassGetTypedefReferent(Dart_Handle clazz) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
- if (cls.IsNull()) {
- RETURN_TYPE_ERROR(isolate, clazz, Class);
- }
-
- if (!cls.IsSignatureClass() && !cls.IsCanonicalSignatureClass()) {
- const String& cls_name = String::Handle(cls.UserVisibleName());
- return Api::NewError("%s: class '%s' is not a typedef class. "
- "See Dart_ClassIsTypedef.",
- CURRENT_FUNC, cls_name.ToCString());
- }
-
- const Function& func = Function::Handle(isolate, cls.signature_function());
- return Api::NewHandle(isolate, func.signature_class());
-}
-
-
-DART_EXPORT bool Dart_ClassIsFunctionType(Dart_Handle clazz) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
- if (cls.IsNull()) {
- RETURN_TYPE_ERROR(isolate, clazz, Class);
- }
- // A class represents a function type when it is a canonical
- // signature class.
- return cls.IsCanonicalSignatureClass();
-}
-
-
-DART_EXPORT Dart_Handle Dart_ClassGetFunctionTypeSignature(Dart_Handle clazz) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
- if (cls.IsNull()) {
- RETURN_TYPE_ERROR(isolate, clazz, Class);
- }
- if (!cls.IsCanonicalSignatureClass()) {
- const String& cls_name = String::Handle(cls.UserVisibleName());
- return Api::NewError("%s: class '%s' is not a function-type class. "
- "See Dart_ClassIsFunctionType.",
- CURRENT_FUNC, cls_name.ToCString());
- }
- return Api::NewHandle(isolate, cls.signature_function());
-}
-
-
-// --- Function and Variable Reflection ---
-
-
-// Outside of the vm, we expose setter names with a trailing '='.
-static bool HasExternalSetterSuffix(const String& name) {
- return name.CharAt(name.Length() - 1) == '=';
-}
-
-
-static RawString* RemoveExternalSetterSuffix(const String& name) {
- ASSERT(HasExternalSetterSuffix(name));
- return String::SubString(name, 0, name.Length() - 1);
-}
-
-
-DART_EXPORT Dart_Handle Dart_GetFunctionNames(Dart_Handle target) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target));
- if (obj.IsError()) {
- return target;
- }
-
- const GrowableObjectArray& names =
- GrowableObjectArray::Handle(isolate, GrowableObjectArray::New());
- Function& func = Function::Handle();
- String& name = String::Handle();
-
- if (obj.IsClass()) {
- const Class& cls = Class::Cast(obj);
- const Error& error = Error::Handle(isolate, cls.EnsureIsFinalized(isolate));
- if (!error.IsNull()) {
- return Api::NewHandle(isolate, error.raw());
- }
- const Array& func_array = Array::Handle(cls.functions());
-
- // Some special types like 'dynamic' have a null functions list.
- if (!func_array.IsNull()) {
- for (intptr_t i = 0; i < func_array.Length(); ++i) {
- func ^= func_array.At(i);
-
- // Skip implicit getters and setters.
- if (func.kind() == RawFunction::kImplicitGetter ||
- func.kind() == RawFunction::kImplicitSetter ||
- func.kind() == RawFunction::kConstImplicitGetter ||
- func.kind() == RawFunction::kMethodExtractor) {
- continue;
- }
-
- name = func.UserVisibleName();
- names.Add(name);
- }
- }
- } else if (obj.IsLibrary()) {
- const Library& lib = Library::Cast(obj);
- DictionaryIterator it(lib);
- Object& obj = Object::Handle();
- while (it.HasNext()) {
- obj = it.GetNext();
- if (obj.IsFunction()) {
- func ^= obj.raw();
- name = func.UserVisibleName();
- names.Add(name);
- }
- }
- } else {
- return Api::NewError(
- "%s expects argument 'target' to be a class or library.",
- CURRENT_FUNC);
- }
- return Api::NewHandle(isolate, Array::MakeArray(names));
-}
-
-
-DART_EXPORT Dart_Handle Dart_LookupFunction(Dart_Handle target,
- Dart_Handle function_name) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target));
- if (obj.IsError()) {
- return target;
- }
- const String& func_name = Api::UnwrapStringHandle(isolate, function_name);
- if (func_name.IsNull()) {
- RETURN_TYPE_ERROR(isolate, function_name, String);
- }
-
- Function& func = Function::Handle(isolate);
- String& tmp_name = String::Handle(isolate);
- if (obj.IsClass()) {
- const Class& cls = Class::Cast(obj);
-
- // Case 1. Lookup the unmodified function name.
- func = cls.LookupFunctionAllowPrivate(func_name);
-
- // Case 2. Lookup the function without the external setter suffix
- // '='. Make sure to do this check after the regular lookup, so
- // that we don't interfere with operator lookups (like ==).
- if (func.IsNull() && HasExternalSetterSuffix(func_name)) {
- tmp_name = RemoveExternalSetterSuffix(func_name);
- tmp_name = Field::SetterName(tmp_name);
- func = cls.LookupFunctionAllowPrivate(tmp_name);
- }
-
- // Case 3. Lookup the funciton with the getter prefix prepended.
- if (func.IsNull()) {
- tmp_name = Field::GetterName(func_name);
- func = cls.LookupFunctionAllowPrivate(tmp_name);
- }
-
- // Case 4. Lookup the function with a . appended to find the
- // unnamed constructor.
- if (func.IsNull()) {
- tmp_name = String::Concat(func_name, Symbols::Dot());
- func = cls.LookupFunctionAllowPrivate(tmp_name);
- }
- } else if (obj.IsLibrary()) {
- const Library& lib = Library::Cast(obj);
-
- // Case 1. Lookup the unmodified function name.
- func = lib.LookupFunctionAllowPrivate(func_name);
-
- // Case 2. Lookup the function without the external setter suffix
- // '='. Make sure to do this check after the regular lookup, so
- // that we don't interfere with operator lookups (like ==).
- if (func.IsNull() && HasExternalSetterSuffix(func_name)) {
- tmp_name = RemoveExternalSetterSuffix(func_name);
- tmp_name = Field::SetterName(tmp_name);
- func = lib.LookupFunctionAllowPrivate(tmp_name);
- }
-
- // Case 3. Lookup the function with the getter prefix prepended.
- if (func.IsNull()) {
- tmp_name = Field::GetterName(func_name);
- func = lib.LookupFunctionAllowPrivate(tmp_name);
- }
- } else {
- return Api::NewError(
- "%s expects argument 'target' to be a class or library.",
- CURRENT_FUNC);
- }
-
-#if defined(DEBUG)
- if (!func.IsNull()) {
- // We only provide access to a subset of function kinds.
- RawFunction::Kind func_kind = func.kind();
- ASSERT(func_kind == RawFunction::kRegularFunction ||
- func_kind == RawFunction::kGetterFunction ||
- func_kind == RawFunction::kSetterFunction ||
- func_kind == RawFunction::kConstructor);
- }
-#endif
- return Api::NewHandle(isolate, func.raw());
-}
-
-
-DART_EXPORT bool Dart_IsFunction(Dart_Handle handle) {
- return Api::ClassId(handle) == kFunctionCid;
-}
-
-
-DART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Function& func = Api::UnwrapFunctionHandle(isolate, function);
- if (func.IsNull()) {
- RETURN_TYPE_ERROR(isolate, function, Function);
- }
- return Api::NewHandle(isolate, func.UserVisibleName());
-}
-
-
-DART_EXPORT Dart_Handle Dart_FunctionOwner(Dart_Handle function) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Function& func = Api::UnwrapFunctionHandle(isolate, function);
- if (func.IsNull()) {
- RETURN_TYPE_ERROR(isolate, function, Function);
- }
- if (func.IsNonImplicitClosureFunction()) {
- RawFunction* parent_function = func.parent_function();
- return Api::NewHandle(isolate, parent_function);
- }
- const Class& owner = Class::Handle(func.Owner());
- ASSERT(!owner.IsNull());
- if (owner.IsTopLevel()) {
- // Top-level functions are implemented as members of a hidden class. We hide
- // that class here and instead answer the library.
-#if defined(DEBUG)
- const Library& lib = Library::Handle(owner.library());
- if (lib.IsNull()) {
- ASSERT(owner.IsDynamicClass() || owner.IsVoidClass());
- }
-#endif
- return Api::NewHandle(isolate, owner.library());
- } else {
- return Api::NewHandle(isolate, owner.raw());
- }
-}
-
-
-DART_EXPORT Dart_Handle Dart_FunctionIsAbstract(Dart_Handle function,
- bool* is_abstract) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- if (is_abstract == NULL) {
- RETURN_NULL_ERROR(is_abstract);
- }
- const Function& func = Api::UnwrapFunctionHandle(isolate, function);
- if (func.IsNull()) {
- RETURN_TYPE_ERROR(isolate, function, Function);
- }
- *is_abstract = func.is_abstract();
- return Api::Success();
-}
-
-
-DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function,
- bool* is_static) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- if (is_static == NULL) {
- RETURN_NULL_ERROR(is_static);
- }
- const Function& func = Api::UnwrapFunctionHandle(isolate, function);
- if (func.IsNull()) {
- RETURN_TYPE_ERROR(isolate, function, Function);
- }
- *is_static = func.is_static();
- return Api::Success();
-}
-
-
-DART_EXPORT Dart_Handle Dart_FunctionIsConstructor(Dart_Handle function,
- bool* is_constructor) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- if (is_constructor == NULL) {
- RETURN_NULL_ERROR(is_constructor);
- }
- const Function& func = Api::UnwrapFunctionHandle(isolate, function);
- if (func.IsNull()) {
- RETURN_TYPE_ERROR(isolate, function, Function);
- }
- *is_constructor = func.kind() == RawFunction::kConstructor;
- return Api::Success();
-}
-
-
-DART_EXPORT Dart_Handle Dart_FunctionIsGetter(Dart_Handle function,
- bool* is_getter) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- if (is_getter == NULL) {
- RETURN_NULL_ERROR(is_getter);
- }
- const Function& func = Api::UnwrapFunctionHandle(isolate, function);
- if (func.IsNull()) {
- RETURN_TYPE_ERROR(isolate, function, Function);
- }
- *is_getter = func.IsGetterFunction();
- return Api::Success();
-}
-
-
-DART_EXPORT Dart_Handle Dart_FunctionIsSetter(Dart_Handle function,
- bool* is_setter) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- if (is_setter == NULL) {
- RETURN_NULL_ERROR(is_setter);
- }
- const Function& func = Api::UnwrapFunctionHandle(isolate, function);
- if (func.IsNull()) {
- RETURN_TYPE_ERROR(isolate, function, Function);
- }
- *is_setter = (func.kind() == RawFunction::kSetterFunction);
- return Api::Success();
-}
-
-
-DART_EXPORT Dart_Handle Dart_FunctionReturnType(Dart_Handle function) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Function& func = Api::UnwrapFunctionHandle(isolate, function);
- if (func.IsNull()) {
- RETURN_TYPE_ERROR(isolate, function, Function);
- }
-
- if (func.kind() == RawFunction::kConstructor) {
- // Special case the return type for constructors. Inside the vm
- // we mark them as returning dynamic, but for the purposes of
- // reflection, they return the type of the class being
- // constructed.
- return Api::NewHandle(isolate, func.Owner());
- } else {
- const AbstractType& return_type =
- AbstractType::Handle(isolate, func.result_type());
- return TypeToHandle(isolate, "Dart_FunctionReturnType", return_type);
- }
-}
-
-
-DART_EXPORT Dart_Handle Dart_FunctionParameterCounts(
- Dart_Handle function,
- int64_t* fixed_param_count,
- int64_t* opt_param_count) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- if (fixed_param_count == NULL) {
- RETURN_NULL_ERROR(fixed_param_count);
- }
- if (opt_param_count == NULL) {
- RETURN_NULL_ERROR(opt_param_count);
- }
- const Function& func = Api::UnwrapFunctionHandle(isolate, function);
- if (func.IsNull()) {
- RETURN_TYPE_ERROR(isolate, function, Function);
- }
-
- // We hide implicit parameters, such as a method's receiver. This is
- // consistent with Invoke or New, which don't expect their callers to
- // provide them in the argument lists they are handed.
- *fixed_param_count = func.num_fixed_parameters() -
- func.NumImplicitParameters();
- // TODO(regis): Separately report named and positional optional param counts.
- *opt_param_count = func.NumOptionalParameters();
-
- ASSERT(*fixed_param_count >= 0);
- ASSERT(*opt_param_count >= 0);
-
- return Api::Success();
-}
-
-
-DART_EXPORT Dart_Handle Dart_FunctionParameterType(Dart_Handle function,
- int parameter_index) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Function& func = Api::UnwrapFunctionHandle(isolate, function);
- if (func.IsNull()) {
- RETURN_TYPE_ERROR(isolate, function, Function);
- }
-
- const intptr_t num_implicit_params = func.NumImplicitParameters();
- const intptr_t num_params = func.NumParameters() - num_implicit_params;
- if (parameter_index < 0 || parameter_index >= num_params) {
- return Api::NewError(
- "%s: argument 'parameter_index' out of range. "
- "Expected 0..%"Pd" but saw %d.",
- CURRENT_FUNC, num_params, parameter_index);
- }
- const AbstractType& param_type =
- AbstractType::Handle(isolate, func.ParameterTypeAt(
- num_implicit_params + parameter_index));
- return TypeToHandle(isolate, "Dart_FunctionParameterType", param_type);
-}
-
-
-DART_EXPORT Dart_Handle Dart_GetVariableNames(Dart_Handle target) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target));
- if (obj.IsError()) {
- return target;
- }
-
- const GrowableObjectArray& names =
- GrowableObjectArray::Handle(isolate, GrowableObjectArray::New());
- Field& field = Field::Handle(isolate);
- String& name = String::Handle(isolate);
-
- if (obj.IsClass()) {
- const Class& cls = Class::Cast(obj);
- const Error& error = Error::Handle(isolate, cls.EnsureIsFinalized(isolate));
- if (!error.IsNull()) {
- return Api::NewHandle(isolate, error.raw());
- }
- const Array& field_array = Array::Handle(cls.fields());
-
- // Some special types like 'dynamic' have a null fields list.
- //
- // TODO(turnidge): Fix 'dynamic' so that it does not have a null
- // fields list. This will have to wait until the empty array is
- // allocated in the vm isolate.
- if (!field_array.IsNull()) {
- for (intptr_t i = 0; i < field_array.Length(); ++i) {
- field ^= field_array.At(i);
- name = field.UserVisibleName();
- names.Add(name);
- }
- }
- } else if (obj.IsLibrary()) {
- const Library& lib = Library::Cast(obj);
- DictionaryIterator it(lib);
- Object& obj = Object::Handle(isolate);
- while (it.HasNext()) {
- obj = it.GetNext();
- if (obj.IsField()) {
- field ^= obj.raw();
- name = field.UserVisibleName();
- names.Add(name);
- }
- }
- } else {
- return Api::NewError(
- "%s expects argument 'target' to be a class or library.",
- CURRENT_FUNC);
- }
- return Api::NewHandle(isolate, Array::MakeArray(names));
-}
-
-
-DART_EXPORT Dart_Handle Dart_LookupVariable(Dart_Handle target,
- Dart_Handle variable_name) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target));
- if (obj.IsError()) {
- return target;
- }
- const String& var_name = Api::UnwrapStringHandle(isolate, variable_name);
- if (var_name.IsNull()) {
- RETURN_TYPE_ERROR(isolate, variable_name, String);
- }
- if (obj.IsClass()) {
- const Class& cls = Class::Cast(obj);
- return Api::NewHandle(isolate, cls.LookupField(var_name));
- }
- if (obj.IsLibrary()) {
- const Library& lib = Library::Cast(obj);
- return Api::NewHandle(isolate, lib.LookupFieldAllowPrivate(var_name));
- }
- return Api::NewError(
- "%s expects argument 'target' to be a class or library.",
- CURRENT_FUNC);
-}
-
-
-DART_EXPORT bool Dart_IsVariable(Dart_Handle handle) {
- return Api::ClassId(handle) == kFieldCid;
-}
-
-
-DART_EXPORT Dart_Handle Dart_VariableName(Dart_Handle variable) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Field& var = Api::UnwrapFieldHandle(isolate, variable);
- if (var.IsNull()) {
- RETURN_TYPE_ERROR(isolate, variable, Field);
- }
- return Api::NewHandle(isolate, var.UserVisibleName());
-}
-
-
-DART_EXPORT Dart_Handle Dart_VariableIsStatic(Dart_Handle variable,
- bool* is_static) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- if (is_static == NULL) {
- RETURN_NULL_ERROR(is_static);
- }
- const Field& var = Api::UnwrapFieldHandle(isolate, variable);
- if (var.IsNull()) {
- RETURN_TYPE_ERROR(isolate, variable, Field);
- }
- *is_static = var.is_static();
- return Api::Success();
-}
-
-
-DART_EXPORT Dart_Handle Dart_VariableIsFinal(Dart_Handle variable,
- bool* is_final) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- if (is_final == NULL) {
- RETURN_NULL_ERROR(is_final);
- }
- const Field& var = Api::UnwrapFieldHandle(isolate, variable);
- if (var.IsNull()) {
- RETURN_TYPE_ERROR(isolate, variable, Field);
- }
- *is_final = var.is_final();
- return Api::Success();
-}
-
-
-DART_EXPORT Dart_Handle Dart_VariableType(Dart_Handle variable) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Field& var = Api::UnwrapFieldHandle(isolate, variable);
- if (var.IsNull()) {
- RETURN_TYPE_ERROR(isolate, variable, Field);
- }
-
- const AbstractType& type = AbstractType::Handle(isolate, var.type());
- return TypeToHandle(isolate, "Dart_VariableType", type);
-}
-
-
-DART_EXPORT Dart_Handle Dart_GetTypeVariableNames(Dart_Handle clazz) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
- if (cls.IsNull()) {
- RETURN_TYPE_ERROR(isolate, clazz, Class);
- }
-
- const intptr_t num_type_params = cls.NumTypeParameters();
- const TypeArguments& type_params =
- TypeArguments::Handle(cls.type_parameters());
-
- const GrowableObjectArray& names =
- GrowableObjectArray::Handle(isolate, GrowableObjectArray::New());
- TypeParameter& type_param = TypeParameter::Handle(isolate);
- String& name = String::Handle(isolate);
- for (intptr_t i = 0; i < num_type_params; i++) {
- type_param ^= type_params.TypeAt(i);
- name = type_param.name();
- names.Add(name);
- }
- return Api::NewHandle(isolate, Array::MakeArray(names));
-}
-
-
-DART_EXPORT Dart_Handle Dart_LookupTypeVariable(
- Dart_Handle clazz,
- Dart_Handle type_variable_name) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
- if (cls.IsNull()) {
- RETURN_TYPE_ERROR(isolate, clazz, Class);
- }
- const String& var_name = Api::UnwrapStringHandle(isolate, type_variable_name);
- if (var_name.IsNull()) {
- RETURN_TYPE_ERROR(isolate, type_variable_name, String);
- }
-
- const intptr_t num_type_params = cls.NumTypeParameters();
- const TypeArguments& type_params =
- TypeArguments::Handle(cls.type_parameters());
-
- TypeParameter& type_param = TypeParameter::Handle(isolate);
- String& name = String::Handle(isolate);
- for (intptr_t i = 0; i < num_type_params; i++) {
- type_param ^= type_params.TypeAt(i);
- name = type_param.name();
- if (name.Equals(var_name)) {
- return Api::NewHandle(isolate, type_param.raw());
- }
- }
- const String& cls_name = String::Handle(cls.UserVisibleName());
- return Api::NewError(
- "%s: Could not find type variable named '%s' for class %s.\n",
- CURRENT_FUNC, var_name.ToCString(), cls_name.ToCString());
-}
-
-
-DART_EXPORT bool Dart_IsTypeVariable(Dart_Handle handle) {
- return Api::ClassId(handle) == kTypeParameterCid;
-}
-
-DART_EXPORT Dart_Handle Dart_TypeVariableName(Dart_Handle type_variable) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const TypeParameter& type_var =
- Api::UnwrapTypeParameterHandle(isolate, type_variable);
- if (type_var.IsNull()) {
- RETURN_TYPE_ERROR(isolate, type_variable, TypeParameter);
- }
- return Api::NewHandle(isolate, type_var.name());
-}
-
-
-DART_EXPORT Dart_Handle Dart_TypeVariableOwner(Dart_Handle type_variable) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const TypeParameter& type_var =
- Api::UnwrapTypeParameterHandle(isolate, type_variable);
- if (type_var.IsNull()) {
- RETURN_TYPE_ERROR(isolate, type_variable, TypeParameter);
- }
- const Class& owner = Class::Handle(type_var.parameterized_class());
- ASSERT(!owner.IsNull() && owner.IsClass());
- return Api::NewHandle(isolate, owner.raw());
-}
-
-
-DART_EXPORT Dart_Handle Dart_TypeVariableUpperBound(Dart_Handle type_variable) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const TypeParameter& type_var =
- Api::UnwrapTypeParameterHandle(isolate, type_variable);
- if (type_var.IsNull()) {
- RETURN_TYPE_ERROR(isolate, type_variable, TypeParameter);
- }
- const AbstractType& bound = AbstractType::Handle(type_var.bound());
- return TypeToHandle(isolate, "Dart_TypeVariableUpperBound", bound);
-}
-
-
-// --- Constructors, Methods, and Fields ---
-
+// --- Invoking Constructors, Methods, and Field accessors ---
static RawObject* ResolveConstructor(const char* current_func,
const Class& cls,
@@ -3879,6 +2953,40 @@
}
+DART_EXPORT Dart_Handle Dart_InvokeClosure(Dart_Handle closure,
+ int number_of_arguments,
+ Dart_Handle* arguments) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ CHECK_CALLBACK_STATE(isolate);
+ const Instance& closure_obj = Api::UnwrapInstanceHandle(isolate, closure);
+ if (closure_obj.IsNull() || !closure_obj.IsCallable(NULL, NULL)) {
+ RETURN_TYPE_ERROR(isolate, closure, Instance);
+ }
+ if (number_of_arguments < 0) {
+ return Api::NewError(
+ "%s expects argument 'number_of_arguments' to be non-negative.",
+ CURRENT_FUNC);
+ }
+ ASSERT(ClassFinalizer::AllClassesFinalized());
+
+ // Set up arguments to include the closure as the first argument.
+ const Array& args = Array::Handle(isolate,
+ Array::New(number_of_arguments + 1));
+ Object& obj = Object::Handle(isolate);
+ args.SetAt(0, closure_obj);
+ for (int i = 0; i < number_of_arguments; i++) {
+ obj = Api::UnwrapHandle(arguments[i]);
+ if (!obj.IsNull() && !obj.IsInstance()) {
+ RETURN_TYPE_ERROR(isolate, arguments[i], Instance);
+ }
+ args.SetAt(i + 1, obj);
+ }
+ // Now try to invoke the closure.
+ return Api::NewHandle(isolate, DartEntry::InvokeClosure(args));
+}
+
+
static bool FieldIsUninitialized(Isolate* isolate, const Field& fld) {
ASSERT(!fld.IsNull());
@@ -4156,6 +3264,85 @@
}
+// --- Exceptions ----
+
+DART_EXPORT Dart_Handle Dart_ThrowException(Dart_Handle exception) {
+ Isolate* isolate = Isolate::Current();
+ CHECK_ISOLATE(isolate);
+ CHECK_CALLBACK_STATE(isolate);
+ {
+ const Instance& excp = Api::UnwrapInstanceHandle(isolate, exception);
+ if (excp.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, exception, Instance);
+ }
+ }
+ if (isolate->top_exit_frame_info() == 0) {
+ // There are no dart frames on the stack so it would be illegal to
+ // throw an exception here.
+ return Api::NewError("No Dart frames on stack, cannot throw exception");
+ }
+
+ // Unwind all the API scopes till the exit frame before throwing an
+ // exception.
+ ApiState* state = isolate->api_state();
+ ASSERT(state != NULL);
+ const Instance* saved_exception;
+ {
+ NoGCScope no_gc;
+ RawInstance* raw_exception =
+ Api::UnwrapInstanceHandle(isolate, exception).raw();
+ state->UnwindScopes(isolate->top_exit_frame_info());
+ saved_exception = &Instance::Handle(raw_exception);
+ }
+ Exceptions::Throw(*saved_exception);
+ return Api::NewError("Exception was not thrown, internal error");
+}
+
+
+DART_EXPORT Dart_Handle Dart_ReThrowException(Dart_Handle exception,
+ Dart_Handle stacktrace) {
+ Isolate* isolate = Isolate::Current();
+ CHECK_ISOLATE(isolate);
+ CHECK_CALLBACK_STATE(isolate);
+ {
+ const Instance& excp = Api::UnwrapInstanceHandle(isolate, exception);
+ if (excp.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, exception, Instance);
+ }
+ const Instance& stk = Api::UnwrapInstanceHandle(isolate, stacktrace);
+ if (stk.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, stacktrace, Instance);
+ }
+ }
+ if (isolate->top_exit_frame_info() == 0) {
+ // There are no dart frames on the stack so it would be illegal to
+ // throw an exception here.
+ return Api::NewError("No Dart frames on stack, cannot throw exception");
+ }
+
+ // Unwind all the API scopes till the exit frame before throwing an
+ // exception.
+ ApiState* state = isolate->api_state();
+ ASSERT(state != NULL);
+ const Instance* saved_exception;
+ const Instance* saved_stacktrace;
+ {
+ NoGCScope no_gc;
+ RawInstance* raw_exception =
+ Api::UnwrapInstanceHandle(isolate, exception).raw();
+ RawInstance* raw_stacktrace =
+ Api::UnwrapInstanceHandle(isolate, stacktrace).raw();
+ state->UnwindScopes(isolate->top_exit_frame_info());
+ saved_exception = &Instance::Handle(raw_exception);
+ saved_stacktrace = &Instance::Handle(raw_stacktrace);
+ }
+ Exceptions::ReThrow(*saved_exception, *saved_stacktrace);
+ return Api::NewError("Exception was not re thrown, internal error");
+}
+
+
+// --- Native fields and functions ---
+
DART_EXPORT Dart_Handle Dart_CreateNativeWrapperClass(Dart_Handle library,
Dart_Handle name,
int field_count) {
@@ -4238,87 +3425,6 @@
}
-// --- Exceptions ----
-
-
-DART_EXPORT Dart_Handle Dart_ThrowException(Dart_Handle exception) {
- Isolate* isolate = Isolate::Current();
- CHECK_ISOLATE(isolate);
- CHECK_CALLBACK_STATE(isolate);
- {
- const Instance& excp = Api::UnwrapInstanceHandle(isolate, exception);
- if (excp.IsNull()) {
- RETURN_TYPE_ERROR(isolate, exception, Instance);
- }
- }
- if (isolate->top_exit_frame_info() == 0) {
- // There are no dart frames on the stack so it would be illegal to
- // throw an exception here.
- return Api::NewError("No Dart frames on stack, cannot throw exception");
- }
-
- // Unwind all the API scopes till the exit frame before throwing an
- // exception.
- ApiState* state = isolate->api_state();
- ASSERT(state != NULL);
- const Instance* saved_exception;
- {
- NoGCScope no_gc;
- RawInstance* raw_exception =
- Api::UnwrapInstanceHandle(isolate, exception).raw();
- state->UnwindScopes(isolate->top_exit_frame_info());
- saved_exception = &Instance::Handle(raw_exception);
- }
- Exceptions::Throw(*saved_exception);
- return Api::NewError("Exception was not thrown, internal error");
-}
-
-
-DART_EXPORT Dart_Handle Dart_ReThrowException(Dart_Handle exception,
- Dart_Handle stacktrace) {
- Isolate* isolate = Isolate::Current();
- CHECK_ISOLATE(isolate);
- CHECK_CALLBACK_STATE(isolate);
- {
- const Instance& excp = Api::UnwrapInstanceHandle(isolate, exception);
- if (excp.IsNull()) {
- RETURN_TYPE_ERROR(isolate, exception, Instance);
- }
- const Instance& stk = Api::UnwrapInstanceHandle(isolate, stacktrace);
- if (stk.IsNull()) {
- RETURN_TYPE_ERROR(isolate, stacktrace, Instance);
- }
- }
- if (isolate->top_exit_frame_info() == 0) {
- // There are no dart frames on the stack so it would be illegal to
- // throw an exception here.
- return Api::NewError("No Dart frames on stack, cannot throw exception");
- }
-
- // Unwind all the API scopes till the exit frame before throwing an
- // exception.
- ApiState* state = isolate->api_state();
- ASSERT(state != NULL);
- const Instance* saved_exception;
- const Instance* saved_stacktrace;
- {
- NoGCScope no_gc;
- RawInstance* raw_exception =
- Api::UnwrapInstanceHandle(isolate, exception).raw();
- RawInstance* raw_stacktrace =
- Api::UnwrapInstanceHandle(isolate, stacktrace).raw();
- state->UnwindScopes(isolate->top_exit_frame_info());
- saved_exception = &Instance::Handle(raw_exception);
- saved_stacktrace = &Instance::Handle(raw_stacktrace);
- }
- Exceptions::ReThrow(*saved_exception, *saved_stacktrace);
- return Api::NewError("Exception was not re thrown, internal error");
-}
-
-
-// --- Native functions ---
-
-
DART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args,
int index) {
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
@@ -4350,31 +3456,8 @@
}
-// --- Metadata ----
-
-DART_EXPORT Dart_Handle Dart_GetMetadata(Dart_Handle object) {
- Isolate* isolate = Isolate::Current();
- CHECK_ISOLATE(isolate);
- DARTSCOPE(isolate);
- const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(object));
- Class& cls = Class::Handle(isolate);
- if (obj.IsClass()) {
- cls ^= obj.raw();
- } else if (obj.IsFunction()) {
- cls = Function::Cast(obj).origin();
- } else if (obj.IsField()) {
- cls = Field::Cast(obj).origin();
- } else {
- return Api::NewHandle(isolate, Object::empty_array().raw());
- }
- const Library& lib = Library::Handle(cls.library());
- return Api::NewHandle(isolate, lib.GetMetadata(obj));
-}
-
-
// --- Scripts and Libraries ---
-
DART_EXPORT Dart_Handle Dart_SetLibraryTagHandler(
Dart_LibraryTagHandler handler) {
Isolate* isolate = Isolate::Current();
@@ -4512,48 +3595,6 @@
}
-static void CompileAll(Isolate* isolate, Dart_Handle* result) {
- ASSERT(isolate != NULL);
- const Error& error = Error::Handle(isolate, Library::CompileAll());
- if (error.IsNull()) {
- *result = Api::Success();
- } else {
- *result = Api::NewHandle(isolate, error.raw());
- }
-}
-
-
-DART_EXPORT Dart_Handle Dart_CompileAll() {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- Dart_Handle result = Api::CheckIsolateState(isolate);
- if (::Dart_IsError(result)) {
- return result;
- }
- CHECK_CALLBACK_STATE(isolate);
- CompileAll(isolate, &result);
- return result;
-}
-
-
-DART_EXPORT Dart_Handle Dart_CheckFunctionFingerprints() {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- Dart_Handle result = Api::CheckIsolateState(isolate);
- if (::Dart_IsError(result)) {
- return result;
- }
- CHECK_CALLBACK_STATE(isolate);
- Library::CheckFunctionFingerprints();
- return result;
-}
-
-
-DART_EXPORT bool Dart_IsLibrary(Dart_Handle object) {
- return Api::ClassId(object) == kLibraryCid;
-}
-
-
DART_EXPORT Dart_Handle Dart_GetClass(Dart_Handle library,
Dart_Handle class_name) {
Isolate* isolate = Isolate::Current();
@@ -4578,19 +3619,6 @@
}
-DART_EXPORT Dart_Handle Dart_LibraryName(Dart_Handle library) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Library& lib = Api::UnwrapLibraryHandle(isolate, library);
- if (lib.IsNull()) {
- RETURN_TYPE_ERROR(isolate, library, Library);
- }
- const String& name = String::Handle(isolate, lib.name());
- ASSERT(!name.IsNull());
- return Api::NewHandle(isolate, name.raw());
-}
-
-
DART_EXPORT Dart_Handle Dart_LibraryUrl(Dart_Handle library) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
@@ -4604,38 +3632,6 @@
}
-DART_EXPORT Dart_Handle Dart_LibraryGetClassNames(Dart_Handle library) {
- Isolate* isolate = Isolate::Current();
- DARTSCOPE(isolate);
- const Library& lib = Api::UnwrapLibraryHandle(isolate, library);
- if (lib.IsNull()) {
- RETURN_TYPE_ERROR(isolate, library, Library);
- }
-
- const GrowableObjectArray& names =
- GrowableObjectArray::Handle(isolate, GrowableObjectArray::New());
- ClassDictionaryIterator it(lib);
- Class& cls = Class::Handle();
- String& name = String::Handle();
- while (it.HasNext()) {
- cls = it.GetNextClass();
- if (cls.IsSignatureClass()) {
- if (!cls.IsCanonicalSignatureClass()) {
- // This is a typedef. Add it to the list of class names.
- name = cls.UserVisibleName();
- names.Add(name);
- } else {
- // Skip canonical signature classes. These are not named.
- }
- } else {
- name = cls.UserVisibleName();
- names.Add(name);
- }
- }
- return Api::NewHandle(isolate, Array::MakeArray(names));
-}
-
-
DART_EXPORT Dart_Handle Dart_LookupLibrary(Dart_Handle url) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
@@ -4813,22 +3809,8 @@
}
-// --- Profiling support ---
-
-// TODO(7565): Dartium should use the new VM flag "generate_pprof_symbols" for
-// pprof profiling. Then these symbols should be removed.
-
-DART_EXPORT void Dart_InitPprofSupport() { }
-
-DART_EXPORT void Dart_GetPprofSymbolInfo(void** buffer, int* buffer_size) {
- *buffer = NULL;
- *buffer_size = 0;
-}
-
-
// --- Peer support ---
-
DART_EXPORT Dart_Handle Dart_GetPeer(Dart_Handle object, void** peer) {
if (peer == NULL) {
RETURN_NULL_ERROR(peer);
diff --git a/runtime/vm/dart_api_impl.h b/runtime/vm/dart_api_impl.h
index 3959611..c3b66e6 100644
--- a/runtime/vm/dart_api_impl.h
+++ b/runtime/vm/dart_api_impl.h
@@ -58,6 +58,39 @@
HANDLESCOPE(__temp_isolate__);
+#define RETURN_TYPE_ERROR(isolate, dart_handle, type) \
+ do { \
+ const Object& tmp = \
+ Object::Handle(isolate, Api::UnwrapHandle((dart_handle))); \
+ if (tmp.IsNull()) { \
+ return Api::NewError("%s expects argument '%s' to be non-null.", \
+ CURRENT_FUNC, #dart_handle); \
+ } else if (tmp.IsError()) { \
+ return dart_handle; \
+ } else { \
+ return Api::NewError("%s expects argument '%s' to be of type %s.", \
+ CURRENT_FUNC, #dart_handle, #type); \
+ } \
+ } while (0)
+
+
+#define RETURN_NULL_ERROR(parameter) \
+ return Api::NewError("%s expects argument '%s' to be non-null.", \
+ CURRENT_FUNC, #parameter);
+
+
+#define CHECK_LENGTH(length, max_elements) \
+ do { \
+ intptr_t len = (length); \
+ intptr_t max = (max_elements); \
+ if (len < 0 || len > max) { \
+ return Api::NewError( \
+ "%s expects argument '%s' to be in the range [0..%"Pd"].", \
+ CURRENT_FUNC, #length, max); \
+ } \
+ } while (0)
+
+
class Api : AllStatic {
public:
// Creates a new local handle.
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 250f714..36ca32d 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -2,8 +2,10 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-#include "include/dart_api.h"
#include "bin/builtin.h"
+#include "include/dart_api.h"
+#include "include/dart_mirrors_api.h"
+#include "include/dart_native_api.h"
#include "platform/assert.h"
#include "platform/json.h"
#include "platform/utils.h"
diff --git a/runtime/vm/dart_api_message.h b/runtime/vm/dart_api_message.h
index 876c474..a2faf44 100644
--- a/runtime/vm/dart_api_message.h
+++ b/runtime/vm/dart_api_message.h
@@ -5,6 +5,7 @@
#ifndef VM_DART_API_MESSAGE_H_
#define VM_DART_API_MESSAGE_H_
+#include "include/dart_native_api.h"
#include "vm/dart_api_state.h"
#include "vm/snapshot.h"
diff --git a/runtime/vm/debugger_mips.cc b/runtime/vm/debugger_mips.cc
index c35df1b..a8721ac 100644
--- a/runtime/vm/debugger_mips.cc
+++ b/runtime/vm/debugger_mips.cc
@@ -48,7 +48,6 @@
Instr* instr5 = Instr::At(pc_ - 1 * Instr::kInstrSize);
#if defined(DEBUG)
-
instr1->AssertIsImmInstr(LW, SP, RA, 2 * kWordSize);
instr2->AssertIsImmInstr(LW, SP, FP, 1 * kWordSize);
instr3->AssertIsImmInstr(LW, SP, PP, 0 * kWordSize);
diff --git a/runtime/vm/disassembler_mips.cc b/runtime/vm/disassembler_mips.cc
index ecbea4f..e97b981 100644
--- a/runtime/vm/disassembler_mips.cc
+++ b/runtime/vm/disassembler_mips.cc
@@ -329,6 +329,14 @@
Format(instr, "mflo 'rd");
break;
}
+ case MOVCI: {
+ if (instr->Bit(16)) {
+ Format(instr, "movt 'rd, 'rs");
+ } else {
+ Format(instr, "movf 'rd, 'rs");
+ }
+ break;
+ }
case MOVN: {
Format(instr, "movn 'rd, 'rs, 'rt");
break;
@@ -523,35 +531,35 @@
break;
}
case COP1_C_F: {
- Format(instr, "c.f.'fmt 'fd, 'fs");
+ Format(instr, "c.f.'fmt 'fs, 'ft");
break;
}
case COP1_C_UN: {
- Format(instr, "c.un.'fmt 'fd, 'fs");
+ Format(instr, "c.un.'fmt 'fs, 'ft");
break;
}
case COP1_C_EQ: {
- Format(instr, "c.eq.'fmt 'fd, 'fs");
+ Format(instr, "c.eq.'fmt 'fs, 'ft");
break;
}
case COP1_C_UEQ: {
- Format(instr, "c.ueq.'fmt 'fd, 'fs");
+ Format(instr, "c.ueq.'fmt 'fs, 'ft");
break;
}
case COP1_C_OLT: {
- Format(instr, "c.olt.'fmt 'fd, 'fs");
+ Format(instr, "c.olt.'fmt 'fs, 'ft");
break;
}
case COP1_C_ULT: {
- Format(instr, "c.ult.'fmt 'fd, 'fs");
+ Format(instr, "c.ult.'fmt 'fs, 'ft");
break;
}
case COP1_C_OLE: {
- Format(instr, "c.ole.'fmt 'fd, 'fs");
+ Format(instr, "c.ole.'fmt 'fs, 'ft");
break;
}
case COP1_C_ULE: {
- Format(instr, "c.ule.'fmt 'fd, 'fs");
+ Format(instr, "c.ule.'fmt 'fs, 'ft");
break;
}
case COP1_CVT_D: {
diff --git a/runtime/vm/flow_graph.cc b/runtime/vm/flow_graph.cc
index 96ef188..5963f70 100644
--- a/runtime/vm/flow_graph.cc
+++ b/runtime/vm/flow_graph.cc
@@ -651,7 +651,8 @@
void FlowGraph::Rename(GrowableArray<PhiInstr*>* live_phis,
VariableLivenessAnalysis* variable_liveness,
ZoneGrowableArray<Definition*>* inlining_parameters) {
- if (!FLAG_optimize_try_catch && (graph_entry_->SuccessorCount() > 1)) {
+ GraphEntryInstr* entry = graph_entry();
+ if (!FLAG_optimize_try_catch && (entry->SuccessorCount() > 1)) {
Bailout("Catch-entry support in SSA.");
}
@@ -672,28 +673,32 @@
env.Add(defn);
}
} else {
- // Create new parameters.
- for (intptr_t i = 0; i < parameter_count(); ++i) {
- ParameterInstr* param = new ParameterInstr(i, graph_entry_);
+ // Create new parameters. For functions compiled for OSR, the locals
+ // are unknown and so treated like parameters.
+ intptr_t count = IsCompiledForOsr() ? variable_count() : parameter_count();
+ for (intptr_t i = 0; i < count; ++i) {
+ ParameterInstr* param = new ParameterInstr(i, entry);
param->set_ssa_temp_index(alloc_ssa_temp_index()); // New SSA temp.
AddToInitialDefinitions(param);
env.Add(param);
}
}
- // Initialize all locals with #null in the renaming environment.
- for (intptr_t i = parameter_count(); i < variable_count(); ++i) {
- env.Add(constant_null());
+ // Initialize all locals with #null in the renaming environment. For OSR,
+ // the locals have already been handled as parameters.
+ if (!IsCompiledForOsr()) {
+ for (intptr_t i = parameter_count(); i < variable_count(); ++i) {
+ env.Add(constant_null());
+ }
}
- if (graph_entry_->SuccessorCount() > 1) {
+ if (entry->SuccessorCount() > 1) {
// Functions with try-catch have a fixed area of stack slots reserved
// so that all local variables are stored at a known location when
// on entry to the catch.
- graph_entry_->set_fixed_slot_count(
- num_stack_locals() + num_copied_params());
+ entry->set_fixed_slot_count(num_stack_locals() + num_copied_params());
}
- RenameRecursive(graph_entry_, &env, live_phis, variable_liveness);
+ RenameRecursive(entry, &env, live_phis, variable_liveness);
}
diff --git a/runtime/vm/flow_graph.h b/runtime/vm/flow_graph.h
index 597ef71..8ed08c8 100644
--- a/runtime/vm/flow_graph.h
+++ b/runtime/vm/flow_graph.h
@@ -181,6 +181,8 @@
loop_invariant_loads_ = loop_invariant_loads;
}
+ bool IsCompiledForOsr() const { return graph_entry()->IsCompiledForOsr(); }
+
private:
friend class IfConverter;
friend class BranchSimplifier;
diff --git a/runtime/vm/flow_graph_allocator.cc b/runtime/vm/flow_graph_allocator.cc
index fddde80..d878f8d 100644
--- a/runtime/vm/flow_graph_allocator.cc
+++ b/runtime/vm/flow_graph_allocator.cc
@@ -510,16 +510,7 @@
Definition* defn = (*catch_entry->initial_definitions())[i];
LiveRange* range = GetLiveRange(defn->ssa_temp_index());
range->DefineAt(catch_entry->start_pos()); // Defined at block entry.
-
- // Save range->End() because it may change in ProcessInitialDefinition.
- intptr_t range_end = range->End();
ProcessInitialDefinition(defn, range, catch_entry);
- spill_slots_.Add(range_end);
- quad_spill_slots_.Add(false);
-
- if (defn->IsParameter() && range->spill_slot().stack_index() >= 0) {
- MarkAsObjectAtSafepoints(range);
- }
}
}
}
@@ -532,16 +523,7 @@
LiveRange* range = GetLiveRange(defn->ssa_temp_index());
range->AddUseInterval(graph_entry->start_pos(), graph_entry->end_pos());
range->DefineAt(graph_entry->start_pos());
-
- // Save range->End() because it may change in ProcessInitialDefinition.
- intptr_t range_end = range->End();
ProcessInitialDefinition(defn, range, graph_entry);
- if (defn->IsParameter() && flow_graph_.num_copied_params() > 0) {
- spill_slots_.Add(range_end);
- quad_spill_slots_.Add(false);
-
- MarkAsObjectAtSafepoints(range);
- }
}
}
@@ -549,6 +531,8 @@
void FlowGraphAllocator::ProcessInitialDefinition(Definition* defn,
LiveRange* range,
BlockEntryInstr* block) {
+ // Save the range end because it may change below.
+ intptr_t range_end = range->End();
if (defn->IsParameter()) {
ParameterInstr* param = defn->AsParameter();
// Assert that copied and non-copied parameters are mutually exclusive.
@@ -579,6 +563,17 @@
CompleteRange(tail, Location::kRegister);
}
ConvertAllUses(range);
+ if (defn->IsParameter() && (range->spill_slot().stack_index() >= 0)) {
+ // Parameters above the frame pointer consume spill slots and are marked
+ // in stack maps.
+ spill_slots_.Add(range_end);
+ quad_spill_slots_.Add(false);
+ MarkAsObjectAtSafepoints(range);
+ } else if (defn->IsConstant() && block->IsCatchBlockEntry()) {
+ // Constants at catch block entries consume spill slots.
+ spill_slots_.Add(range_end);
+ quad_spill_slots_.Add(false);
+ }
}
@@ -1617,7 +1612,6 @@
}
}
-
// Set spill slot expiration boundary to the live range's end.
spill_slots_[idx] = end;
if (need_quad) {
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index b869940..c8ae244 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -6,6 +6,7 @@
#include "lib/invocation_mirror.h"
#include "vm/ast_printer.h"
+#include "vm/bit_vector.h"
#include "vm/code_descriptors.h"
#include "vm/dart_entry.h"
#include "vm/flags.h"
@@ -43,7 +44,8 @@
FlowGraphBuilder::FlowGraphBuilder(ParsedFunction* parsed_function,
const Array& ic_data_array,
- InlineExitCollector* exit_collector)
+ InlineExitCollector* exit_collector,
+ intptr_t osr_id)
: parsed_function_(parsed_function),
ic_data_array_(ic_data_array),
num_copied_params_(parsed_function->num_copied_params()),
@@ -58,7 +60,8 @@
last_used_try_index_(CatchClauseNode::kInvalidTryIndex),
try_index_(CatchClauseNode::kInvalidTryIndex),
graph_entry_(NULL),
- args_pushed_(0) { }
+ args_pushed_(0),
+ osr_id_(osr_id) { }
void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) {
@@ -474,7 +477,8 @@
}
-void EffectGraphVisitor::TieLoop(const TestGraphVisitor& test_fragment,
+void EffectGraphVisitor::TieLoop(intptr_t token_pos,
+ const TestGraphVisitor& test_fragment,
const EffectGraphVisitor& body_fragment) {
// We have: a test graph fragment with zero, one, or two available exits;
// and an effect graph fragment with zero or one available exits. We want
@@ -494,14 +498,16 @@
} else {
JoinEntryInstr* join =
new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index());
- join->LinkTo(test_fragment.entry());
+ CheckStackOverflowInstr* check =
+ new CheckStackOverflowInstr(token_pos, true);
+ join->LinkTo(check);
+ check->LinkTo(test_fragment.entry());
Goto(join);
body_exit->Goto(join);
}
// 3. Set the exit to the graph to be the false successor of the test, a
// fresh target node
-
exit_ = test_fragment.CreateFalseSuccessor();
}
@@ -1602,8 +1608,6 @@
ASSERT(!for_test.is_empty()); // Language spec.
EffectGraphVisitor for_body(owner(), temp_index());
- for_body.AddInstruction(
- new CheckStackOverflowInstr(node->token_pos()));
node->body()->Visit(&for_body);
// Labels are set after body traversal.
@@ -1614,7 +1618,7 @@
if (for_body.is_open()) for_body.Goto(join);
for_body.exit_ = join;
}
- TieLoop(for_test, for_body);
+ TieLoop(node->token_pos(), for_test, for_body);
join = lbl->join_for_break();
if (join != NULL) {
Goto(join);
@@ -1634,8 +1638,6 @@
void EffectGraphVisitor::VisitDoWhileNode(DoWhileNode* node) {
// Traverse body first in order to generate continue and break labels.
EffectGraphVisitor for_body(owner(), temp_index());
- for_body.AddInstruction(
- new CheckStackOverflowInstr(node->token_pos()));
node->body()->Visit(&for_body);
TestGraphVisitor for_test(owner(),
@@ -1657,7 +1659,10 @@
join = new JoinEntryInstr(owner()->AllocateBlockId(),
owner()->try_index());
}
- join->LinkTo(for_test.entry());
+ CheckStackOverflowInstr* check =
+ new CheckStackOverflowInstr(node->token_pos(), true);
+ join->LinkTo(check);
+ check->LinkTo(for_test.entry());
if (body_exit != NULL) {
body_exit->Goto(join);
}
@@ -1694,39 +1699,27 @@
// Compose body to set any jump labels.
EffectGraphVisitor for_body(owner(), temp_index());
- for_body.AddInstruction(
- new CheckStackOverflowInstr(node->token_pos()));
node->body()->Visit(&for_body);
- // Join loop body, increment and compute their end instruction.
- ASSERT(!for_body.is_empty());
- Instruction* loop_increment_end = NULL;
EffectGraphVisitor for_increment(owner(), temp_index());
node->increment()->Visit(&for_increment);
- JoinEntryInstr* join = node->label()->join_for_continue();
- if (join != NULL) {
- // Insert the join between the body and increment.
- if (for_body.is_open()) for_body.Goto(join);
- loop_increment_end = AppendFragment(join, for_increment);
- ASSERT(loop_increment_end != NULL);
- } else if (for_body.is_open()) {
- // Do not insert an extra basic block.
- for_body.Append(for_increment);
- loop_increment_end = for_body.exit();
- // 'for_body' contains at least the stack check.
- ASSERT(loop_increment_end != NULL);
- } else {
- loop_increment_end = NULL;
- }
- // 'loop_increment_end' is NULL only if there is no join for continue and the
- // body is not open, i.e., no backward branch exists.
- if (loop_increment_end != NULL) {
+ // Join the loop body and increment and then tie the loop.
+ JoinEntryInstr* join = node->label()->join_for_continue();
+ if ((join != NULL) || for_body.is_open()) {
JoinEntryInstr* loop_start =
new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index());
+ if (join != NULL) {
+ if (for_body.is_open()) for_body.Goto(join);
+ AppendFragment(join, for_increment);
+ for_increment.Goto(loop_start);
+ } else {
+ for_body.Append(for_increment);
+ for_body.Goto(loop_start);
+ }
Goto(loop_start);
- loop_increment_end->Goto(loop_start);
exit_ = loop_start;
+ AddInstruction(new CheckStackOverflowInstr(node->token_pos(), true));
}
if (node->condition() == NULL) {
@@ -3463,16 +3456,15 @@
// Print the function ast before IL generation.
AstPrinter::PrintFunctionNodes(*parsed_function());
}
- // Compilation can be nested, preserve the computation-id.
const Function& function = parsed_function()->function();
TargetEntryInstr* normal_entry =
new TargetEntryInstr(AllocateBlockId(),
CatchClauseNode::kInvalidTryIndex);
- graph_entry_ = new GraphEntryInstr(*parsed_function(), normal_entry);
+ graph_entry_ = new GraphEntryInstr(*parsed_function(), normal_entry, osr_id_);
EffectGraphVisitor for_effect(this, 0);
// This check may be deleted if the generated code is leaf.
CheckStackOverflowInstr* check =
- new CheckStackOverflowInstr(function.token_pos());
+ new CheckStackOverflowInstr(function.token_pos(), false);
// If we are inlining don't actually attach the stack check. We must still
// create the stack check in order to allocate a deopt id.
if (!IsInlining()) for_effect.AddInstruction(check);
@@ -3480,11 +3472,31 @@
AppendFragment(normal_entry, for_effect);
// Check that the graph is properly terminated.
ASSERT(!for_effect.is_open());
+
+ // When compiling for OSR, use a depth first search to prune instructions
+ // unreachable from the OSR entry. Catch entries are not (yet) properly
+ // recognized as reachable.
+ if (osr_id_ != Isolate::kNoDeoptId) {
+ if (graph_entry_->SuccessorCount() > 1) {
+ Bailout("try/catch when compiling for OSR");
+ }
+ PruneUnreachable();
+ }
+
FlowGraph* graph = new FlowGraph(*this, graph_entry_, last_used_block_id_);
return graph;
}
+void FlowGraphBuilder::PruneUnreachable() {
+ ASSERT(osr_id_ != Isolate::kNoDeoptId);
+ BitVector* block_marks = new BitVector(last_used_block_id_ + 1);
+ bool found = graph_entry_->PruneUnreachable(this, graph_entry_, osr_id_,
+ block_marks);
+ ASSERT(found);
+}
+
+
void FlowGraphBuilder::Bailout(const char* reason) {
const char* kFormat = "FlowGraphBuilder Bailout: %s %s";
const char* function_name = parsed_function_->function().ToCString();
diff --git a/runtime/vm/flow_graph_builder.h b/runtime/vm/flow_graph_builder.h
index 3a1c685..2ae1401 100644
--- a/runtime/vm/flow_graph_builder.h
+++ b/runtime/vm/flow_graph_builder.h
@@ -100,10 +100,12 @@
// Build a flow graph from a parsed function's AST.
class FlowGraphBuilder: public ValueObject {
public:
- // The inlining context is NULL if not inlining.
+ // The inlining context is NULL if not inlining. The osr_id is the deopt
+ // id of the OSR entry or Isolate::kNoDeoptId if not compiling for OSR.
FlowGraphBuilder(ParsedFunction* parsed_function,
const Array& ic_data_array,
- InlineExitCollector* exit_collector);
+ InlineExitCollector* exit_collector,
+ intptr_t osr_id);
FlowGraph* BuildGraph();
@@ -143,6 +145,10 @@
intptr_t args_pushed() const { return args_pushed_; }
void add_args_pushed(intptr_t n) { args_pushed_ += n; }
+ // When compiling for OSR, remove blocks that are not reachable from the
+ // OSR entry point.
+ void PruneUnreachable();
+
private:
intptr_t parameter_count() const {
return num_copied_params_ + num_non_copied_params_;
@@ -168,6 +174,10 @@
// Outgoing argument stack height.
intptr_t args_pushed_;
+ // The deopt id of the OSR entry or Isolate::kNoDeoptId if not compiling
+ // for OSR.
+ const intptr_t osr_id_;
+
DISALLOW_IMPLICIT_CONSTRUCTORS(FlowGraphBuilder);
};
@@ -229,7 +239,8 @@
// Append a 'while loop' test and back edge to this graph, depending on
// which parts are reachable. Afterward, the graph exit is the false
// successor of the loop condition.
- void TieLoop(const TestGraphVisitor& test_fragment,
+ void TieLoop(intptr_t token_pos,
+ const TestGraphVisitor& test_fragment,
const EffectGraphVisitor& body_fragment);
// Wraps a value in a push-argument instruction and adds the result to the
diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc
index dafa68c..07cc65a 100644
--- a/runtime/vm/flow_graph_compiler.cc
+++ b/runtime/vm/flow_graph_compiler.cc
@@ -101,19 +101,25 @@
pc_descriptors_list_ = new DescriptorList(64);
exception_handlers_list_ = new ExceptionHandlerList();
block_info_.Clear();
- bool is_leaf = !parsed_function().function().IsClosureFunction() &&
- is_optimizing();
+ // Conservative detection of leaf routines used to remove the stack check
+ // on function entry.
+ bool is_leaf = !parsed_function().function().IsClosureFunction()
+ && is_optimizing()
+ && !flow_graph().IsCompiledForOsr();
+ // Initialize block info and search optimized (non-OSR) code for calls
+ // indicating a non-leaf routine and calls without IC data indicating
+ // possible reoptimization.
for (int i = 0; i < block_order_.length(); ++i) {
block_info_.Add(new BlockInfo());
- if (is_optimizing()) {
+ if (is_optimizing() && !flow_graph().IsCompiledForOsr()) {
BlockEntryInstr* entry = block_order_[i];
for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
Instruction* current = it.Current();
- const ICData* ic_data = NULL;
if (current->IsBranch()) {
current = current->AsBranch()->comparison();
}
// In optimized code, ICData is always set in the instructions.
+ const ICData* ic_data = NULL;
if (current->IsInstanceCall()) {
ic_data = current->AsInstanceCall()->ic_data();
ASSERT(ic_data != NULL);
@@ -126,10 +132,9 @@
}
if ((ic_data != NULL) && (ic_data->NumberOfChecks() == 0)) {
may_reoptimize_ = true;
- break;
}
if (is_leaf && !current->IsCheckStackOverflow()) {
- // Note that we do no care if the code contains instructions that
+ // Note that we do not care if the code contains instructions that
// can deoptimize.
LocationSummary* locs = current->locs();
if ((locs != NULL) && locs->can_call()) {
@@ -140,11 +145,10 @@
}
}
if (is_leaf) {
- // Remove check stack overflow at entry.
- CheckStackOverflowInstr* check = flow_graph_.graph_entry()->normal_entry()
- ->next()->AsCheckStackOverflow();
- ASSERT(check != NULL);
- check->RemoveFromGraph();
+ // Remove the stack overflow check at function entry.
+ Instruction* first = flow_graph_.graph_entry()->normal_entry()->next();
+ ASSERT(first->IsCheckStackOverflow());
+ if (first->IsCheckStackOverflow()) first->RemoveFromGraph();
}
}
@@ -553,7 +557,9 @@
}
// Emit IC call that will count and thus may need reoptimization at
// function entry.
- ASSERT(!is_optimizing() || may_reoptimize());
+ ASSERT(!is_optimizing()
+ || may_reoptimize()
+ || flow_graph().IsCompiledForOsr());
switch (ic_data.num_args_tested()) {
case 1:
label_address = StubCode::OneArgOptimizedCheckInlineCacheEntryPoint();
diff --git a/runtime/vm/flow_graph_compiler_ia32.cc b/runtime/vm/flow_graph_compiler_ia32.cc
index d282034..fe154c7 100644
--- a/runtime/vm/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/flow_graph_compiler_ia32.cc
@@ -24,6 +24,7 @@
DEFINE_FLAG(bool, trap_on_deoptimization, false, "Trap on deoptimization.");
DEFINE_FLAG(bool, unbox_mints, true, "Optimize 64-bit integer arithmetic.");
DECLARE_FLAG(int, optimization_counter_threshold);
+DECLARE_FLAG(int, reoptimization_counter_threshold);
DECLARE_FLAG(bool, print_ast);
DECLARE_FLAG(bool, print_scopes);
DECLARE_FLAG(bool, enable_type_checks);
@@ -1077,34 +1078,43 @@
void FlowGraphCompiler::EmitFrameEntry() {
const Function& function = parsed_function().function();
- if (CanOptimizeFunction() && function.is_optimizable()) {
- const bool can_optimize = !is_optimizing() || may_reoptimize();
+ if (CanOptimizeFunction() &&
+ function.is_optimizable() &&
+ (!is_optimizing() || may_reoptimize())) {
const Register function_reg = EDI;
- if (can_optimize) {
- __ LoadObject(function_reg, function);
- }
+ __ LoadObject(function_reg, function);
// Patch point is after the eventually inlined function object.
AddCurrentDescriptor(PcDescriptors::kEntryPatch,
Isolate::kNoDeoptId,
0); // No token position.
- if (can_optimize) {
- // Reoptimization of optimized function is triggered by counting in
+ if (is_optimizing()) {
+ // Reoptimization of an optimized function is triggered by counting in
// IC stubs, but not at the entry of the function.
- if (!is_optimizing()) {
- __ incl(FieldAddress(function_reg, Function::usage_counter_offset()));
- }
__ cmpl(FieldAddress(function_reg, Function::usage_counter_offset()),
- Immediate(FLAG_optimization_counter_threshold));
- ASSERT(function_reg == EDI);
- __ j(GREATER_EQUAL, &StubCode::OptimizeFunctionLabel());
+ Immediate(FLAG_reoptimization_counter_threshold));
+ } else {
+ __ incl(FieldAddress(function_reg, Function::usage_counter_offset()));
+ __ cmpl(FieldAddress(function_reg, Function::usage_counter_offset()),
+ Immediate(FLAG_optimization_counter_threshold));
}
- } else {
+ ASSERT(function_reg == EDI);
+ __ j(GREATER_EQUAL, &StubCode::OptimizeFunctionLabel());
+ } else if (!flow_graph().IsCompiledForOsr()) {
AddCurrentDescriptor(PcDescriptors::kEntryPatch,
Isolate::kNoDeoptId,
0); // No token position.
}
__ Comment("Enter frame");
- __ EnterDartFrame(StackSize() * kWordSize);
+ if (flow_graph().IsCompiledForOsr()) {
+ intptr_t extra_slots = StackSize()
+ - flow_graph().num_stack_locals()
+ - flow_graph().num_copied_params();
+ ASSERT(extra_slots >= 0);
+ __ EnterOsrFrame(extra_slots * kWordSize);
+ } else {
+ ASSERT(StackSize() >= 0);
+ __ EnterDartFrame(StackSize() * kWordSize);
+ }
}
@@ -1136,9 +1146,10 @@
if (num_copied_params == 0) {
#ifdef DEBUG
ASSERT(!parsed_function().function().HasOptionalParameters());
- const bool check_arguments = true;
+ const bool check_arguments = !flow_graph().IsCompiledForOsr();
#else
- const bool check_arguments = function.IsClosureFunction();
+ const bool check_arguments =
+ function.IsClosureFunction() && !flow_graph().IsCompiledForOsr();
#endif
if (check_arguments) {
__ Comment("Check argument count");
@@ -1196,7 +1207,7 @@
// The arguments descriptor is never saved in the absence of optional
// parameters, since any argument definition test would always yield true.
ASSERT(saved_args_desc_var == NULL);
- } else {
+ } else if (!flow_graph().IsCompiledForOsr()) {
if (saved_args_desc_var != NULL) {
__ Comment("Save arguments descriptor");
const Register kArgumentsDescriptorReg = EDX;
diff --git a/runtime/vm/flow_graph_compiler_mips.cc b/runtime/vm/flow_graph_compiler_mips.cc
index 27abae0..a088cc6 100644
--- a/runtime/vm/flow_graph_compiler_mips.cc
+++ b/runtime/vm/flow_graph_compiler_mips.cc
@@ -166,7 +166,8 @@
Label* is_false) {
__ TraceSimMsg("BoolToJump");
Label fall_through;
- __ beq(bool_register, NULLREG, &fall_through);
+ __ BranchEqual(bool_register, reinterpret_cast<int32_t>(Object::null()),
+ &fall_through);
__ BranchEqual(bool_register, Bool::True(), is_true);
__ b(is_false);
__ Bind(&fall_through);
@@ -190,12 +191,12 @@
__ LoadObject(A2, type_test_cache);
if (test_kind == kTestTypeOneArg) {
ASSERT(type_arguments_reg == kNoRegister);
+ __ LoadImmediate(A1, reinterpret_cast<int32_t>(Object::null()));
__ BranchLink(&StubCode::Subtype1TestCacheLabel());
- __ delay_slot()->mov(A1, NULLREG);
} else if (test_kind == kTestTypeTwoArgs) {
ASSERT(type_arguments_reg == kNoRegister);
+ __ LoadImmediate(A1, reinterpret_cast<int32_t>(Object::null()));
__ BranchLink(&StubCode::Subtype2TestCacheLabel());
- __ delay_slot()->mov(A1, NULLREG);
} else if (test_kind == kTestTypeThreeArgs) {
ASSERT(type_arguments_reg == A1);
__ BranchLink(&StubCode::Subtype3TestCacheLabel());
@@ -329,7 +330,8 @@
// Check if instance is a closure.
__ LoadClassById(T1, kClassIdReg);
__ lw(T1, FieldAddress(T1, Class::signature_function_offset()));
- __ bne(T1, NULLREG, is_instance_lbl);
+ __ BranchNotEqual(T1, reinterpret_cast<int32_t>(Object::null()),
+ is_instance_lbl);
}
// Custom checking for numbers (Smi, Mint, Bigint and Double).
// Note that instance is not Smi (checked above).
@@ -397,7 +399,8 @@
__ lw(A1, Address(SP, 0)); // Get instantiator type arguments.
// A1: instantiator type arguments.
// Check if type argument is dynamic.
- __ beq(A1, NULLREG, is_instance_lbl);
+ __ LoadImmediate(T7, reinterpret_cast<int32_t>(Object::null()));
+ __ beq(A1, T7, is_instance_lbl);
// Can handle only type arguments that are instances of TypeArguments.
// (runtime checks canonicalize type arguments).
Label fall_through;
@@ -408,7 +411,7 @@
// R2: concrete type of type.
// Check if type argument is dynamic.
__ BranchEqual(T2, Type::ZoneHandle(Type::DynamicType()), is_instance_lbl);
- __ beq(T2, NULLREG, is_instance_lbl);
+ __ beq(T2, T7, is_instance_lbl);
const Type& object_type = Type::ZoneHandle(Type::ObjectType());
__ BranchEqual(T2, object_type, is_instance_lbl);
@@ -483,11 +486,11 @@
if (TypeCheckAsClassEquality(type)) {
const intptr_t type_cid = Class::Handle(type.type_class()).id();
const Register kInstanceReg = A0;
- __ andi(T0, kInstanceReg, Immediate(kSmiTagMask));
+ __ andi(CMPRES, kInstanceReg, Immediate(kSmiTagMask));
if (type_cid == kSmiCid) {
- __ beq(T0, ZR, is_instance_lbl);
+ __ beq(CMPRES, ZR, is_instance_lbl);
} else {
- __ beq(T0, ZR, is_not_instance_lbl);
+ __ beq(CMPRES, ZR, is_not_instance_lbl);
__ LoadClassId(T0, kInstanceReg);
__ BranchEqual(T0, type_cid, is_instance_lbl);
}
@@ -527,12 +530,90 @@
}
+// If instanceof type test cannot be performed successfully at compile time and
+// therefore eliminated, optimize it by adding inlined tests for:
+// - NULL -> return false.
+// - Smi -> compile time subtype check (only if dst class is not parameterized).
+// - Class equality (only if class is not parameterized).
+// Inputs:
+// - A0: object.
+// - A1: instantiator type arguments or raw_null.
+// - A2: instantiator or raw_null.
+// Returns:
+// - true or false in V0.
void FlowGraphCompiler::GenerateInstanceOf(intptr_t token_pos,
intptr_t deopt_id,
const AbstractType& type,
bool negate_result,
LocationSummary* locs) {
- UNIMPLEMENTED();
+ ASSERT(type.IsFinalized() && !type.IsMalformed());
+
+ // Preserve instantiator (A2) and its type arguments (A1).
+ __ addiu(SP, SP, Immediate(-2 * kWordSize));
+ __ sw(A2, Address(SP, 1 * kWordSize));
+ __ sw(A1, Address(SP, 0 * kWordSize));
+
+ Label is_instance, is_not_instance;
+ // If type is instantiated and non-parameterized, we can inline code
+ // checking whether the tested instance is a Smi.
+ if (type.IsInstantiated()) {
+ // A null object is only an instance of Object and dynamic, which has
+ // already been checked above (if the type is instantiated). So we can
+ // return false here if the instance is null (and if the type is
+ // instantiated).
+ // We can only inline this null check if the type is instantiated at compile
+ // time, since an uninstantiated type at compile time could be Object or
+ // dynamic at run time.
+ __ BranchEqual(A0, reinterpret_cast<int32_t>(Object::null()),
+ &is_not_instance);
+ }
+
+ // Generate inline instanceof test.
+ SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle();
+ test_cache = GenerateInlineInstanceof(token_pos, type,
+ &is_instance, &is_not_instance);
+
+ // test_cache is null if there is no fall-through.
+ Label done;
+ if (!test_cache.IsNull()) {
+ // Generate runtime call.
+ // Load instantiator (A2) and its type arguments (A1).
+ __ lw(A1, Address(SP, 0 * kWordSize));
+ __ lw(A2, Address(SP, 1 * kWordSize));
+
+ __ addiu(SP, SP, Immediate(-6 * kWordSize));
+ __ LoadObject(TMP, Object::ZoneHandle());
+ __ sw(TMP, Address(SP, 5 * kWordSize)); // Make room for the result.
+ __ sw(A0, Address(SP, 4 * kWordSize)); // Push the instance.
+ __ LoadObject(TMP, type);
+ __ sw(TMP, Address(SP, 3 * kWordSize)); // Push the type.
+ __ sw(A2, Address(SP, 2 * kWordSize)); // Push instantiator.
+ __ sw(A1, Address(SP, 1 * kWordSize)); // Push type arguments.
+ __ LoadObject(A0, test_cache);
+ __ sw(A0, Address(SP, 0 * kWordSize));
+ GenerateCallRuntime(token_pos, deopt_id, kInstanceofRuntimeEntry, locs);
+ // Pop the parameters supplied to the runtime entry. The result of the
+ // instanceof runtime call will be left as the result of the operation.
+ __ lw(T0, Address(SP, 5 * kWordSize));
+ __ addiu(SP, SP, Immediate(6 * kWordSize));
+ if (negate_result) {
+ __ LoadObject(V0, Bool::True());
+ __ bne(T0, V0, &done);
+ __ LoadObject(V0, Bool::False());
+ } else {
+ __ mov(V0, T0);
+ }
+ __ b(&done);
+ }
+ __ Bind(&is_not_instance);
+ __ LoadObject(V0, negate_result ? Bool::True() : Bool::False());
+ __ b(&done);
+
+ __ Bind(&is_instance);
+ __ LoadObject(V0, negate_result ? Bool::False() : Bool::True());
+ __ Bind(&done);
+ // Remove instantiator (A2) and its type arguments (A1).
+ __ Drop(2);
}
@@ -567,7 +648,8 @@
// A null object is always assignable and is returned as result.
Label is_assignable, runtime_call;
- __ beq(A0, NULLREG, &is_assignable);
+
+ __ BranchEqual(A0, reinterpret_cast<int32_t>(Object::null()), &is_assignable);
__ delay_slot()->sw(A1, Address(SP, 0 * kWordSize));
if (!FLAG_eliminate_type_checks) {
@@ -804,7 +886,8 @@
delete[] opt_param_position;
// Check that T0 now points to the null terminator in the array descriptor.
__ lw(T3, Address(T0));
- __ beq(T3, NULLREG, &all_arguments_processed);
+ __ BranchEqual(T3, reinterpret_cast<int32_t>(Object::null()),
+ &all_arguments_processed);
} else {
ASSERT(num_opt_pos_params > 0);
__ Comment("There are optional positional parameters");
@@ -894,8 +977,9 @@
__ Bind(&null_args_loop);
__ addiu(T2, T2, Immediate(-kWordSize));
__ addu(T3, T1, T2);
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
__ bgtz(T2, &null_args_loop);
- __ delay_slot()->sw(NULLREG, Address(T3));
+ __ delay_slot()->sw(TMP, Address(T3));
__ Bind(&null_args_loop_exit);
}
@@ -918,8 +1002,9 @@
__ lw(T0, Address(SP, 1 * kWordSize)); // Receiver.
__ lw(T1, Address(SP, 0 * kWordSize)); // Value.
__ StoreIntoObject(T0, FieldAddress(T0, offset), T1);
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
__ Ret();
- __ delay_slot()->mov(V0, NULLREG);
+ __ delay_slot()->mov(V0, TMP);
}
@@ -1113,7 +1198,8 @@
const intptr_t slot_base = parsed_function().first_stack_local_index();
for (intptr_t i = 0; i < num_locals; ++i) {
// Subtract index i (locals lie at lower addresses than FP).
- __ sw(NULLREG, Address(FP, (slot_base - i) * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
+ __ sw(TMP, Address(FP, (slot_base - i) * kWordSize));
}
}
@@ -1348,11 +1434,11 @@
__ addiu(SP, SP, Immediate(-2 * kWordSize));
__ sw(reg, Address(SP, 1 * kWordSize));
__ LoadObject(TMP1, obj);
+ __ sw(TMP1, Address(SP, 0 * kWordSize));
__ BranchLink(&StubCode::IdenticalWithNumberCheckLabel());
AddCurrentDescriptor(PcDescriptors::kRuntimeCall,
Isolate::kNoDeoptId,
token_pos);
- __ delay_slot()->sw(TMP1, Address(SP, 0 * kWordSize));
__ TraceSimMsg("EqualityRegConstCompare return");
__ lw(reg, Address(SP, 1 * kWordSize)); // Restore 'reg'.
__ addiu(SP, SP, Immediate(2 * kWordSize)); // Discard constant.
@@ -1367,14 +1453,15 @@
bool needs_number_check,
intptr_t token_pos) {
__ TraceSimMsg("EqualityRegRegCompare");
+ __ Comment("EqualityRegRegCompare");
if (needs_number_check) {
__ addiu(SP, SP, Immediate(-2 * kWordSize));
__ sw(left, Address(SP, 1 * kWordSize));
+ __ sw(right, Address(SP, 0 * kWordSize));
__ BranchLink(&StubCode::IdenticalWithNumberCheckLabel());
AddCurrentDescriptor(PcDescriptors::kRuntimeCall,
Isolate::kNoDeoptId,
token_pos);
- __ delay_slot()->sw(right, Address(SP, 0 * kWordSize));
__ TraceSimMsg("EqualityRegRegCompare return");
// Stub returns result in CMPRES. If it is 0, then left and right are equal.
__ lw(right, Address(SP, 0 * kWordSize));
@@ -1395,8 +1482,9 @@
__ TraceSimMsg("SuperEqualityCallPrologue");
__ lw(result, Address(SP, 0 * kWordSize)); // Load right operand.
__ lw(TMP1, Address(SP, 1 * kWordSize)); // Load left operand.
- __ beq(result, NULLREG, &check_identity); // Is right null?
- __ bne(TMP1, NULLREG, &fall_through); // If right is non-null, check left.
+ __ LoadImmediate(CMPRES, reinterpret_cast<int32_t>(Object::null()));
+ __ beq(result, CMPRES, &check_identity); // Is right null?
+ __ bne(TMP1, CMPRES, &fall_through); // If right is non-null, check left.
__ Bind(&check_identity);
__ bne(result, TMP1, &is_false);
@@ -1414,9 +1502,20 @@
void FlowGraphCompiler::SaveLiveRegisters(LocationSummary* locs) {
__ TraceSimMsg("SaveLiveRegisters");
// TODO(vegorov): consider saving only caller save (volatile) registers.
- const intptr_t fpu_registers = locs->live_registers()->fpu_registers();
- if (fpu_registers > 0) {
- UNIMPLEMENTED();
+ const intptr_t fpu_regs_count= locs->live_registers()->fpu_regs_count();
+ if (fpu_regs_count > 0) {
+ __ AddImmediate(SP, -(fpu_regs_count * kFpuRegisterSize));
+ // Store fpu registers with the lowest register number at the lowest
+ // address.
+ intptr_t offset = 0;
+ for (intptr_t reg_idx = 0; reg_idx < kNumberOfFpuRegisters; ++reg_idx) {
+ DRegister fpu_reg = static_cast<DRegister>(reg_idx);
+ if (locs->live_registers()->ContainsFpuRegister(fpu_reg)) {
+ __ StoreDToOffset(fpu_reg, SP, offset);
+ offset += kFpuRegisterSize;
+ }
+ }
+ ASSERT(offset == (fpu_regs_count * kFpuRegisterSize));
}
// Store general purpose registers with the lowest register number at the
@@ -1455,9 +1554,19 @@
}
__ addiu(SP, SP, Immediate(register_count * kWordSize));
- const intptr_t fpu_registers = locs->live_registers()->fpu_registers();
- if (fpu_registers > 0) {
- UNIMPLEMENTED();
+ const intptr_t fpu_regs_count = locs->live_registers()->fpu_regs_count();
+ if (fpu_regs_count > 0) {
+ // Fpu registers have the lowest register number at the lowest address.
+ intptr_t offset = 0;
+ for (intptr_t reg_idx = 0; reg_idx < kNumberOfFpuRegisters; ++reg_idx) {
+ DRegister fpu_reg = static_cast<DRegister>(reg_idx);
+ if (locs->live_registers()->ContainsFpuRegister(fpu_reg)) {
+ __ LoadDFromOffset(fpu_reg, SP, offset);
+ offset += kFpuRegisterSize;
+ }
+ }
+ ASSERT(offset == (fpu_regs_count * kFpuRegisterSize));
+ __ AddImmediate(SP, offset);
}
}
@@ -1481,6 +1590,7 @@
Array::ZoneHandle(ArgumentsDescriptor::New(argument_count,
argument_names));
__ TraceSimMsg("EmitTestAndCall");
+ __ Comment("EmitTestAndCall");
__ LoadObject(S4, arguments_descriptor);
for (intptr_t i = 0; i < len; i++) {
const bool is_last_check = (i == (len - 1));
@@ -1513,7 +1623,33 @@
FpuRegister left,
FpuRegister right,
BranchInstr* branch) {
- UNIMPLEMENTED();
+ ASSERT(branch != NULL);
+ __ Comment("DoubleCompareBranch");
+ assembler()->cund(left, right);
+ BlockEntryInstr* nan_result = (true_condition == NE) ?
+ branch->true_successor() : branch->false_successor();
+ assembler()->bc1t(GetJumpLabel(nan_result));
+
+ switch (true_condition) {
+ case EQ: assembler()->ceqd(left, right); break;
+ case LT: assembler()->coltd(left, right); break;
+ case LE: assembler()->coled(left, right); break;
+ case GT: assembler()->coltd(right, left); break;
+ case GE: assembler()->coled(right, left); break;
+ default: {
+ // Should only passing the above conditions to this function.
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ assembler()->LoadImmediate(TMP, 1);
+ assembler()->movf(CMPRES, TMP);
+ assembler()->movt(CMPRES, ZR);
+ assembler()->mov(TMP, ZR);
+
+ // EmitBranchOnCondition expects ordering to be described by CMPRES, TMP1.
+ branch->EmitBranchOnCondition(this, EQ);
}
@@ -1521,7 +1657,28 @@
FpuRegister left,
FpuRegister right,
Register result) {
- UNIMPLEMENTED();
+ Label done;
+ __ Comment("DoubleCompareBool");
+ assembler()->LoadObject(result, Bool::False());
+ assembler()->cund(left, right);
+ assembler()->bc1t(&done);
+
+ switch (true_condition) {
+ case EQ: assembler()->ceqd(left, right); break;
+ case LT: assembler()->coltd(left, right); break;
+ case LE: assembler()->coled(left, right); break;
+ case GT: assembler()->coltd(right, left); break;
+ case GE: assembler()->coled(right, left); break;
+ default: {
+ // Should only passing the above conditions to this function.
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ assembler()->bc1f(&done); // False is already in result.
+ assembler()->LoadObject(result, Bool::True());
+ assembler()->Bind(&done);
}
@@ -1591,7 +1748,9 @@
__ movd(destination.fpu_reg(), source.fpu_reg());
} else {
if (destination.IsDoubleStackSlot()) {
- __ sdc1(source.fpu_reg(), destination.ToStackSlotAddress());
+ const Address& addr = destination.ToStackSlotAddress();
+ int32_t offset = addr.offset();
+ __ StoreDToOffset(source.fpu_reg(), FP, offset);
} else {
ASSERT(destination.IsQuadStackSlot());
UNIMPLEMENTED();
@@ -1599,11 +1758,18 @@
}
} else if (source.IsDoubleStackSlot()) {
if (destination.IsFpuRegister()) {
- __ ldc1(destination.fpu_reg(), source.ToStackSlotAddress());
+ const Address &addr = source.ToStackSlotAddress();
+ const Register base = addr.base();
+ const int32_t offset = addr.offset();
+ __ LoadDFromOffset(destination.fpu_reg(), base, offset);
} else {
ASSERT(destination.IsDoubleStackSlot());
- __ ldc1(FpuTMP, source.ToStackSlotAddress());
- __ sdc1(FpuTMP, destination.ToStackSlotAddress());
+ const Address& saddr = source.ToStackSlotAddress();
+ const Address& daddr = destination.ToStackSlotAddress();
+ int32_t soffset = saddr.offset();
+ int32_t doffset = daddr.offset();
+ __ LoadDFromOffset(FpuTMP, FP, soffset);
+ __ StoreDToOffset(FpuTMP, FP, doffset);
}
} else if (source.IsQuadStackSlot()) {
UNIMPLEMENTED();
@@ -1658,8 +1824,10 @@
: source.ToStackSlotAddress();
if (double_width) {
- __ ldc1(FpuTMP, slot_address);
- __ sdc1(reg, slot_address);
+ const Register base = slot_address.base();
+ const int32_t offset = slot_address.offset();
+ __ LoadDFromOffset(FpuTMP, base, offset);
+ __ StoreDToOffset(reg, base, offset);
__ movd(reg, FpuTMP);
} else {
UNIMPLEMENTED();
@@ -1667,12 +1835,16 @@
} else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
const Address& source_slot_address = source.ToStackSlotAddress();
const Address& destination_slot_address = destination.ToStackSlotAddress();
+ const Register sbase = source_slot_address.base();
+ const int32_t soffset = source_slot_address.offset();
+ const Register dbase = destination_slot_address.base();
+ const int32_t doffset = destination_slot_address.offset();
ScratchFpuRegisterScope ensure_scratch(this, FpuTMP);
- __ ldc1(FpuTMP, source_slot_address);
- __ ldc1(ensure_scratch.reg(), destination_slot_address);
- __ sdc1(FpuTMP, destination_slot_address);
- __ sdc1(ensure_scratch.reg(), source_slot_address);
+ __ LoadDFromOffset(FpuTMP, sbase, soffset);
+ __ LoadDFromOffset(ensure_scratch.reg(), dbase, doffset);
+ __ StoreDToOffset(FpuTMP, dbase, doffset);
+ __ StoreDToOffset(ensure_scratch.reg(), sbase, soffset);
} else if (source.IsQuadStackSlot() && destination.IsQuadStackSlot()) {
UNIMPLEMENTED();
} else {
@@ -1746,13 +1918,13 @@
void ParallelMoveResolver::SpillFpuScratch(FpuRegister reg) {
__ TraceSimMsg("ParallelMoveResolver::SpillFpuScratch");
__ AddImmediate(SP, -kDoubleSize);
- __ sdc1(reg, Address(SP));
+ __ StoreDToOffset(reg, SP, 0);
}
void ParallelMoveResolver::RestoreFpuScratch(FpuRegister reg) {
__ TraceSimMsg("ParallelMoveResolver::RestoreFpuScratch");
- __ ldc1(reg, Address(SP));
+ __ LoadDFromOffset(reg, SP, 0);
__ AddImmediate(SP, kDoubleSize);
}
diff --git a/runtime/vm/flow_graph_compiler_x64.cc b/runtime/vm/flow_graph_compiler_x64.cc
index d15ecea..df39bc48 100644
--- a/runtime/vm/flow_graph_compiler_x64.cc
+++ b/runtime/vm/flow_graph_compiler_x64.cc
@@ -23,6 +23,7 @@
DEFINE_FLAG(bool, trap_on_deoptimization, false, "Trap on deoptimization.");
DECLARE_FLAG(int, optimization_counter_threshold);
+DECLARE_FLAG(int, reoptimization_counter_threshold);
DECLARE_FLAG(bool, print_ast);
DECLARE_FLAG(bool, print_scopes);
DECLARE_FLAG(bool, enable_type_checks);
@@ -1071,34 +1072,43 @@
void FlowGraphCompiler::EmitFrameEntry() {
const Function& function = parsed_function().function();
- if (CanOptimizeFunction() && function.is_optimizable()) {
- const bool can_optimize = !is_optimizing() || may_reoptimize();
+ if (CanOptimizeFunction() &&
+ function.is_optimizable() &&
+ (!is_optimizing() || may_reoptimize())) {
const Register function_reg = RDI;
- if (can_optimize) {
- __ LoadObject(function_reg, function);
- }
+ __ LoadObject(function_reg, function);
// Patch point is after the eventually inlined function object.
AddCurrentDescriptor(PcDescriptors::kEntryPatch,
Isolate::kNoDeoptId,
0); // No token position.
- if (can_optimize) {
- // Reoptimization of optimized function is triggered by counting in
+ if (is_optimizing()) {
+ // Reoptimization of an optimized function is triggered by counting in
// IC stubs, but not at the entry of the function.
- if (!is_optimizing()) {
- __ incq(FieldAddress(function_reg, Function::usage_counter_offset()));
- }
__ cmpq(FieldAddress(function_reg, Function::usage_counter_offset()),
- Immediate(FLAG_optimization_counter_threshold));
- ASSERT(function_reg == RDI);
- __ j(GREATER_EQUAL, &StubCode::OptimizeFunctionLabel());
+ Immediate(FLAG_reoptimization_counter_threshold));
+ } else {
+ __ incq(FieldAddress(function_reg, Function::usage_counter_offset()));
+ __ cmpq(FieldAddress(function_reg, Function::usage_counter_offset()),
+ Immediate(FLAG_optimization_counter_threshold));
}
- } else {
+ ASSERT(function_reg == RDI);
+ __ j(GREATER_EQUAL, &StubCode::OptimizeFunctionLabel());
+ } else if (!flow_graph().IsCompiledForOsr()) {
AddCurrentDescriptor(PcDescriptors::kEntryPatch,
- Isolate::kNoDeoptId,
- 0); // No token position.
+ Isolate::kNoDeoptId,
+ 0); // No token position.
}
__ Comment("Enter frame");
- __ EnterDartFrame(StackSize() * kWordSize);
+ if (flow_graph().IsCompiledForOsr()) {
+ intptr_t extra_slots = StackSize()
+ - flow_graph().num_stack_locals()
+ - flow_graph().num_copied_params();
+ ASSERT(extra_slots >= 0);
+ __ EnterOsrFrame(extra_slots * kWordSize);
+ } else {
+ ASSERT(StackSize() >= 0);
+ __ EnterDartFrame(StackSize() * kWordSize);
+ }
}
@@ -1131,9 +1141,10 @@
if (num_copied_params == 0) {
#ifdef DEBUG
ASSERT(!parsed_function().function().HasOptionalParameters());
- const bool check_arguments = true;
+ const bool check_arguments = !flow_graph().IsCompiledForOsr();
#else
- const bool check_arguments = function.IsClosureFunction();
+ const bool check_arguments =
+ function.IsClosureFunction() && !flow_graph().IsCompiledForOsr();
#endif
if (check_arguments) {
__ Comment("Check argument count");
@@ -1191,7 +1202,7 @@
// The arguments descriptor is never saved in the absence of optional
// parameters, since any argument definition test would always yield true.
ASSERT(saved_args_desc_var == NULL);
- } else {
+ } else if (!flow_graph().IsCompiledForOsr()) {
if (saved_args_desc_var != NULL) {
__ Comment("Save arguments descriptor");
const Register kArgumentsDescriptorReg = R10;
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc
index 911e62c..6d86710 100644
--- a/runtime/vm/flow_graph_inliner.cc
+++ b/runtime/vm/flow_graph_inliner.cc
@@ -494,7 +494,10 @@
// Build the callee graph.
InlineExitCollector* exit_collector =
new InlineExitCollector(caller_graph_, call);
- FlowGraphBuilder builder(parsed_function, ic_data_array, exit_collector);
+ FlowGraphBuilder builder(parsed_function,
+ ic_data_array,
+ exit_collector,
+ Isolate::kNoDeoptId);
builder.SetInitialBlockId(caller_graph_->max_block_id());
FlowGraph* callee_graph;
{
diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc
index 0ffb7f0..7366821 100644
--- a/runtime/vm/flow_graph_type_propagator.cc
+++ b/runtime/vm/flow_graph_type_propagator.cc
@@ -691,8 +691,14 @@
// However there are parameters that are known to match their declared type:
// for example receiver and construction phase.
GraphEntryInstr* graph_entry = block_->AsGraphEntry();
- // Parameters at catch-blocks have type dynamic.
- if (graph_entry == NULL) return CompileType::Dynamic();
+ // Parameters at catch blocks and OSR entries have type dynamic.
+ //
+ // TODO(kmillikin): Use the actual type of the parameter at OSR entry.
+ // The code below is not safe for OSR because it doesn't necessarily use
+ // the correct scope.
+ if ((graph_entry == NULL) || graph_entry->IsCompiledForOsr()) {
+ return CompileType::Dynamic();
+ }
const Function& function = graph_entry->parsed_function().function();
LocalScope* scope = graph_entry->parsed_function().node_sequence()->scope();
diff --git a/runtime/vm/il_printer.cc b/runtime/vm/il_printer.cc
index 1a4f025..47fa64b 100644
--- a/runtime/vm/il_printer.cc
+++ b/runtime/vm/il_printer.cc
@@ -860,6 +860,11 @@
}
+void CheckStackOverflowInstr::PrintOperandsTo(BufferFormatter* f) const {
+ if (in_loop()) f->Print("loop");
+}
+
+
void TargetEntryInstr::PrintTo(BufferFormatter* f) const {
f->Print("B%"Pd"[target]:%"Pd, block_id(), GetDeoptId());
if (HasParallelMove()) {
diff --git a/runtime/vm/instructions_mips.cc b/runtime/vm/instructions_mips.cc
index 41648b1..197efd1 100644
--- a/runtime/vm/instructions_mips.cc
+++ b/runtime/vm/instructions_mips.cc
@@ -6,6 +6,7 @@
#if defined(TARGET_ARCH_MIPS)
#include "vm/constants_mips.h"
+#include "vm/cpu.h"
#include "vm/instructions.h"
#include "vm/object.h"
@@ -158,6 +159,24 @@
}
+void CallPattern::InsertAt(uword pc, uword target_address) {
+ Instr* lui = Instr::At(pc + (0 * Instr::kInstrSize));
+ Instr* ori = Instr::At(pc + (1 * Instr::kInstrSize));
+ Instr* jr = Instr::At(pc + (2 * Instr::kInstrSize));
+ Instr* nop = Instr::At(pc + (3 * Instr::kInstrSize));
+ uint16_t target_lo = target_address & 0xffff;
+ uint16_t target_hi = target_address >> 16;
+
+ lui->SetImmInstrBits(LUI, ZR, TMP1, target_hi);
+ ori->SetImmInstrBits(ORI, TMP1, TMP1, target_lo);
+ jr->SetSpecialInstrBits(JALR, TMP1, ZR, RA);
+ nop->SetInstructionBits(Instr::kNopInstruction);
+
+ ASSERT(kFixedLengthInBytes == 4 * Instr::kInstrSize);
+ CPU::FlushICache(pc, kFixedLengthInBytes);
+}
+
+
JumpPattern::JumpPattern(uword pc) : pc_(pc) { }
diff --git a/runtime/vm/instructions_mips.h b/runtime/vm/instructions_mips.h
index 5567ae6..f9d7908 100644
--- a/runtime/vm/instructions_mips.h
+++ b/runtime/vm/instructions_mips.h
@@ -27,7 +27,9 @@
// This constant length is only valid for inserted call patterns used for
// lazy deoptimization. Regular call pattern may vary in length.
- static const int kFixedLengthInBytes = 3 * Instr::kInstrSize;
+ static const int kFixedLengthInBytes = 4 * Instr::kInstrSize;
+
+ static void InsertAt(uword pc, uword target_address);
private:
uword Back(int n) const;
diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc
index 8cc2bcd..87e5407 100644
--- a/runtime/vm/intermediate_language.cc
+++ b/runtime/vm/intermediate_language.cc
@@ -245,12 +245,14 @@
GraphEntryInstr::GraphEntryInstr(const ParsedFunction& parsed_function,
- TargetEntryInstr* normal_entry)
+ TargetEntryInstr* normal_entry,
+ intptr_t osr_id)
: BlockEntryInstr(0, CatchClauseNode::kInvalidTryIndex),
parsed_function_(parsed_function),
normal_entry_(normal_entry),
catch_entries_(),
initial_definitions_(),
+ osr_id_(osr_id),
spill_slot_count_(0),
fixed_slot_count_(0) {
}
@@ -787,6 +789,53 @@
}
+bool BlockEntryInstr::PruneUnreachable(FlowGraphBuilder* builder,
+ GraphEntryInstr* graph_entry,
+ intptr_t osr_id,
+ BitVector* block_marks) {
+ // Search for the instruction with the OSR id. Use a depth first search
+ // because basic blocks have not been discovered yet. Prune unreachable
+ // blocks by replacing the normal entry with a jump to the block
+ // containing the OSR entry point.
+
+ // Do not visit blocks more than once.
+ if (block_marks->Contains(block_id())) return false;
+ block_marks->Add(block_id());
+
+ // Search this block for the OSR id.
+ Instruction* instr = this;
+ for (ForwardInstructionIterator it(this); !it.Done(); it.Advance()) {
+ instr = it.Current();
+ if (instr->GetDeoptId() == osr_id) {
+ // Sanity check that we found a stack check instruction.
+ ASSERT(instr->IsCheckStackOverflow());
+ // Loop stack check checks are always in join blocks so that they can
+ // be the target of a goto.
+ ASSERT(IsJoinEntry());
+ // The instruction should be the first instruction in the block so
+ // we can simply jump to the beginning of the block.
+ ASSERT(instr->previous() == this);
+
+ GotoInstr* goto_join = new GotoInstr(AsJoinEntry());
+ goto_join->deopt_id_ = deopt_id_;
+ graph_entry->normal_entry()->LinkTo(goto_join);
+ return true;
+ }
+ }
+
+ // Recursively search the successors.
+ for (intptr_t i = instr->SuccessorCount() - 1; i >= 0; --i) {
+ if (instr->SuccessorAt(i)->PruneUnreachable(builder,
+ graph_entry,
+ osr_id,
+ block_marks)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
bool BlockEntryInstr::Dominates(BlockEntryInstr* other) const {
// TODO(fschneider): Make this faster by e.g. storing dominators for each
// block while computing the dominator tree.
diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h
index 0ad33b9..2285325 100644
--- a/runtime/vm/intermediate_language.h
+++ b/runtime/vm/intermediate_language.h
@@ -23,6 +23,7 @@
class Definition;
class Environment;
class FlowGraph;
+class FlowGraphBuilder;
class FlowGraphCompiler;
class FlowGraphVisitor;
class Instruction;
@@ -868,7 +869,7 @@
private:
friend class Definition; // Needed for InsertBefore, InsertAfter.
- // Classes that set deopt_id_.
+ // Classes that set or read deopt_id_.
friend class UnboxIntegerInstr;
friend class UnboxDoubleInstr;
friend class UnboxFloat32x4Instr;
@@ -921,6 +922,7 @@
friend class SmiToDoubleInstr;
friend class DoubleToIntegerInstr;
friend class BranchSimplifier;
+ friend class BlockEntryInstr;
virtual void RawSetInputAt(intptr_t i, Value* value) = 0;
@@ -1143,6 +1145,13 @@
intptr_t variable_count,
intptr_t fixed_parameter_count);
+ // Perform a depth first search to prune code not reachable from an OSR
+ // entry point.
+ bool PruneUnreachable(FlowGraphBuilder* builder,
+ GraphEntryInstr* graph_entry,
+ intptr_t osr_id,
+ BitVector* block_marks);
+
virtual intptr_t InputCount() const { return 0; }
virtual Value* InputAt(intptr_t i) const {
UNREACHABLE();
@@ -1281,7 +1290,8 @@
class GraphEntryInstr : public BlockEntryInstr {
public:
GraphEntryInstr(const ParsedFunction& parsed_function,
- TargetEntryInstr* normal_entry);
+ TargetEntryInstr* normal_entry,
+ intptr_t osr_id);
DECLARE_INSTRUCTION(GraphEntry)
@@ -1302,6 +1312,8 @@
}
ConstantInstr* constant_null();
+ bool IsCompiledForOsr() const { return osr_id_ != Isolate::kNoDeoptId; }
+
intptr_t spill_slot_count() const { return spill_slot_count_; }
void set_spill_slot_count(intptr_t count) {
ASSERT(count >= 0);
@@ -1336,6 +1348,7 @@
TargetEntryInstr* normal_entry_;
GrowableArray<CatchBlockEntryInstr*> catch_entries_;
GrowableArray<Definition*> initial_definitions_;
+ const intptr_t osr_id_;
intptr_t spill_slot_count_;
intptr_t fixed_slot_count_; // For try-catch in optimized code.
@@ -5907,10 +5920,11 @@
class CheckStackOverflowInstr : public TemplateInstruction<0> {
public:
- explicit CheckStackOverflowInstr(intptr_t token_pos)
- : token_pos_(token_pos) {}
+ CheckStackOverflowInstr(intptr_t token_pos, bool in_loop)
+ : token_pos_(token_pos), in_loop_(in_loop) {}
intptr_t token_pos() const { return token_pos_; }
+ bool in_loop() const { return in_loop_; }
DECLARE_INSTRUCTION(CheckStackOverflow)
@@ -5922,8 +5936,11 @@
virtual bool MayThrow() const { return false; }
+ virtual void PrintOperandsTo(BufferFormatter* f) const;
+
private:
const intptr_t token_pos_;
+ const bool in_loop_;
DISALLOW_COPY_AND_ASSIGN(CheckStackOverflowInstr);
};
diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc
index e6eb45b..444cd5c 100644
--- a/runtime/vm/intermediate_language_ia32.cc
+++ b/runtime/vm/intermediate_language_ia32.cc
@@ -23,6 +23,7 @@
DECLARE_FLAG(int, optimization_counter_threshold);
DECLARE_FLAG(bool, propagate_ic_data);
+DECLARE_FLAG(bool, use_osr);
// Generic summary for call instructions that have all arguments pushed
// on the stack and return the result in a fixed register EAX.
@@ -2122,6 +2123,13 @@
instruction_->deopt_id(),
kStackOverflowRuntimeEntry,
instruction_->locs());
+
+ if (FLAG_use_osr && !compiler->is_optimizing() && instruction_->in_loop()) {
+ // In unoptimized code, record loop stack checks as possible OSR entries.
+ compiler->AddCurrentDescriptor(PcDescriptors::kOsrEntry,
+ instruction_->deopt_id(),
+ 0); // No token position.
+ }
compiler->pending_deoptimization_env_ = NULL;
compiler->RestoreLiveRegisters(instruction_->locs());
__ jmp(exit_label());
@@ -2139,6 +2147,14 @@
__ cmpl(ESP,
Address::Absolute(Isolate::Current()->stack_limit_address()));
__ j(BELOW_EQUAL, slow_path->entry_label());
+ if (FLAG_use_osr && !compiler->is_optimizing() && in_loop()) {
+ // In unoptimized code check the usage counter to trigger OSR at loop
+ // stack checks.
+ __ LoadObject(EDI, compiler->parsed_function().function());
+ __ cmpl(FieldAddress(EDI, Function::usage_counter_offset()),
+ Immediate(2 * FLAG_optimization_counter_threshold));
+ __ j(GREATER_EQUAL, slow_path->entry_label());
+ }
__ Bind(slow_path->exit_label());
}
diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc
index 8bf386e..813378d 100644
--- a/runtime/vm/intermediate_language_mips.cc
+++ b/runtime/vm/intermediate_language_mips.cc
@@ -387,11 +387,13 @@
const int kNumArgumentsChecked = 2;
__ TraceSimMsg("EmitEqualityAsInstanceCall");
+ __ Comment("EmitEqualityAsInstanceCall");
Label check_identity;
__ lw(A1, Address(SP, 1 * kWordSize));
__ lw(A0, Address(SP, 0 * kWordSize));
- __ beq(A1, NULLREG, &check_identity);
- __ beq(A0, NULLREG, &check_identity);
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
+ __ beq(A1, TMP, &check_identity);
+ __ beq(A0, TMP, &check_identity);
ICData& equality_ic_data = ICData::ZoneHandle();
if (compiler->is_optimizing() && FLAG_propagate_ic_data) {
@@ -536,6 +538,7 @@
Register temp = locs->temp(0).reg();
__ TraceSimMsg("EmitEqualityAsPolymorphicCall");
+ __ Comment("EmitEqualityAsPolymorphicCall");
LoadValueCid(compiler, temp, left,
(ic_data.GetReceiverClassIdAt(0) == kSmiCid) ? NULL : deopt);
@@ -608,12 +611,57 @@
// Emit code when ICData's targets are all Object == (which is ===).
static void EmitCheckedStrictEqual(FlowGraphCompiler* compiler,
- const ICData& ic_data,
+ const ICData& orig_ic_data,
const LocationSummary& locs,
Token::Kind kind,
BranchInstr* branch,
intptr_t deopt_id) {
- UNIMPLEMENTED();
+ ASSERT((kind == Token::kEQ) || (kind == Token::kNE));
+ Register left = locs.in(0).reg();
+ Register right = locs.in(1).reg();
+ Register temp = locs.temp(0).reg();
+ Label* deopt = compiler->AddDeoptStub(deopt_id, kDeoptEquality);
+
+ __ Comment("CheckedStrictEqual");
+
+ __ andi(CMPRES, left, Immediate(kSmiTagMask));
+ __ beq(CMPRES, ZR, deopt);
+ // 'left' is not Smi.
+ Label identity_compare;
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
+ __ beq(right, TMP, &identity_compare);
+ __ beq(left, TMP, &identity_compare);
+
+ __ LoadClassId(temp, left);
+ const ICData& ic_data = ICData::Handle(orig_ic_data.AsUnaryClassChecks());
+ const intptr_t len = ic_data.NumberOfChecks();
+ for (intptr_t i = 0; i < len; i++) {
+ if (i == (len - 1)) {
+ __ BranchNotEqual(temp, ic_data.GetReceiverClassIdAt(i), deopt);
+ } else {
+ __ BranchEqual(temp, ic_data.GetReceiverClassIdAt(i), &identity_compare);
+ }
+ }
+ __ Bind(&identity_compare);
+ __ subu(CMPRES, left, right);
+ if (branch == NULL) {
+ Label done, is_equal;
+ Register result = locs.out().reg();
+ __ beq(CMPRES, ZR, &is_equal);
+ // Not equal.
+ __ LoadObject(result,
+ (kind == Token::kEQ) ? Bool::False() : Bool::True());
+ __ b(&done);
+ __ Bind(&is_equal);
+ __ LoadObject(result,
+ (kind == Token::kEQ) ? Bool::True() : Bool::False());
+ __ Bind(&done);
+
+ } else {
+ Condition cond = TokenKindToSmiCondition(kind);
+ __ mov(TMP, ZR);
+ branch->EmitBranchOnCondition(compiler, cond);
+ }
}
@@ -633,8 +681,10 @@
Register right = locs->in(1).reg();
Label done, identity_compare, non_null_compare;
__ TraceSimMsg("EmitGenericEqualityCompare");
- __ beq(right, NULLREG, &identity_compare);
- __ bne(left, NULLREG, &non_null_compare);
+ __ Comment("EmitGenericEqualityCompare");
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
+ __ beq(right, TMP, &identity_compare);
+ __ bne(left, TMP, &non_null_compare);
// Comparison with NULL is "===".
__ Bind(&identity_compare);
@@ -685,6 +735,7 @@
Token::Kind kind,
BranchInstr* branch) {
__ TraceSimMsg("EmitSmiComparisonOp");
+ __ Comment("EmitSmiComparisonOp");
Location left = locs.in(0);
Location right = locs.in(1);
ASSERT(!left.IsConstant() || !right.IsConstant());
@@ -732,17 +783,45 @@
}
+static Condition TokenKindToDoubleCondition(Token::Kind kind) {
+ switch (kind) {
+ case Token::kEQ: return EQ;
+ case Token::kNE: return NE;
+ case Token::kLT: return LT;
+ case Token::kGT: return GT;
+ case Token::kLTE: return LE;
+ case Token::kGTE: return GE;
+ default:
+ UNREACHABLE();
+ return VS;
+ }
+}
+
+
static void EmitDoubleComparisonOp(FlowGraphCompiler* compiler,
const LocationSummary& locs,
Token::Kind kind,
BranchInstr* branch) {
- UNIMPLEMENTED();
+ DRegister left = locs.in(0).fpu_reg();
+ DRegister right = locs.in(1).fpu_reg();
+
+ __ Comment("DoubleComparisonOp(left=%d, right=%d)", left, right);
+
+ Condition true_condition = TokenKindToDoubleCondition(kind);
+ if (branch != NULL) {
+ compiler->EmitDoubleCompareBranch(
+ true_condition, left, right, branch);
+ } else {
+ compiler->EmitDoubleCompareBool(
+ true_condition, left, right, locs.out().reg());
+ }
}
void EqualityCompareInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT((kind() == Token::kNE) || (kind() == Token::kEQ));
BranchInstr* kNoBranch = NULL;
+ __ Comment("EqualityCompareInstr");
if (receiver_class_id() == kSmiCid) {
EmitSmiComparisonOp(compiler, *locs(), kind(), kNoBranch);
return;
@@ -785,6 +864,7 @@
void EqualityCompareInstr::EmitBranchCode(FlowGraphCompiler* compiler,
BranchInstr* branch) {
__ TraceSimMsg("EqualityCompareInstr");
+ __ Comment("EqualityCompareInstr:BranchCode");
ASSERT((kind() == Token::kNE) || (kind() == Token::kEQ));
if (receiver_class_id() == kSmiCid) {
// Deoptimizes if both arguments not Smi.
@@ -1001,7 +1081,9 @@
// into the runtime system.
uword entry = reinterpret_cast<uword>(native_c_function());
#if defined(USING_SIMULATOR)
- entry = Simulator::RedirectExternalReference(entry, Simulator::kNativeCall);
+ entry = Simulator::RedirectExternalReference(entry,
+ Simulator::kNativeCall,
+ function().NumParameters());
#endif
__ LoadImmediate(T5, entry);
__ LoadImmediate(A1, NativeArguments::ComputeArgcTag(function()));
@@ -1205,7 +1287,28 @@
if ((representation() == kUnboxedDouble) ||
(representation() == kUnboxedMint) ||
(representation() == kUnboxedFloat32x4)) {
- UNIMPLEMENTED();
+ DRegister result = locs()->out().fpu_reg();
+ switch (class_id()) {
+ case kTypedDataInt32ArrayCid:
+ UNIMPLEMENTED();
+ break;
+ case kTypedDataUint32ArrayCid:
+ UNIMPLEMENTED();
+ break;
+ case kTypedDataFloat32ArrayCid:
+ // Load single precision float and promote to double.
+ __ lwc1(STMP1, element_address);
+ __ cvtds(result, STMP1);
+ break;
+ case kTypedDataFloat64ArrayCid:
+ __ LoadDFromOffset(result, index.reg(),
+ FlowGraphCompiler::DataOffsetFor(class_id()) - kHeapObjectTag);
+ break;
+ case kTypedDataFloat32x4ArrayCid:
+ UNIMPLEMENTED();
+ break;
+ }
+ return;
}
Register result = locs()->out().reg();
@@ -1582,7 +1685,8 @@
if (field().is_nullable() && (field_cid != kNullCid)) {
__ beq(CMPRES, ZR, &ok);
- __ subu(CMPRES, value_reg, NULLREG);
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
+ __ subu(CMPRES, value_reg, TMP);
}
if (ok_is_fall_through) {
@@ -1703,13 +1807,30 @@
LocationSummary* InstanceOfInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ const intptr_t kNumInputs = 3;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
+ summary->set_in(0, Location::RegisterLocation(A0));
+ summary->set_in(1, Location::RegisterLocation(A2));
+ summary->set_in(2, Location::RegisterLocation(A1));
+ summary->set_out(Location::RegisterLocation(V0));
+ return summary;
}
void InstanceOfInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ ASSERT(locs()->in(0).reg() == A0); // Value.
+ ASSERT(locs()->in(1).reg() == A2); // Instantiator.
+ ASSERT(locs()->in(2).reg() == A1); // Instantiator type arguments.
+
+ __ Comment("InstanceOfInstr");
+ compiler->GenerateInstanceOf(token_pos(),
+ deopt_id(),
+ type(),
+ negate_result(),
+ locs());
+ ASSERT(locs()->out().reg() == V0);
}
@@ -1739,14 +1860,19 @@
LocationSummary*
AllocateObjectWithBoundsCheckInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ return MakeCallSummary();
}
void AllocateObjectWithBoundsCheckInstr::EmitNativeCode(
FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ compiler->GenerateCallRuntime(token_pos(),
+ deopt_id(),
+ kAllocateObjectWithBoundsCheckRuntimeEntry,
+ locs());
+ __ Drop(3);
+ ASSERT(locs()->out().reg() == V0);
+ __ Pop(V0); // Pop new instance.
}
@@ -1793,7 +1919,8 @@
Label type_arguments_instantiated;
const intptr_t len = type_arguments().Length();
if (type_arguments().IsRawInstantiatedRaw(len)) {
- __ beq(instantiator_reg, NULLREG, &type_arguments_instantiated);
+ __ BranchEqual(instantiator_reg, reinterpret_cast<int32_t>(Object::null()),
+ &type_arguments_instantiated);
}
// Instantiate non-null type arguments.
// A runtime call to instantiate the type arguments is required.
@@ -1846,7 +1973,8 @@
// the type arguments.
Label type_arguments_instantiated;
ASSERT(type_arguments().IsRawInstantiatedRaw(type_arguments().Length()));
- __ beq(instantiator_reg, NULLREG, &type_arguments_instantiated);
+ __ BranchEqual(instantiator_reg, reinterpret_cast<int32_t>(Object::null()),
+ &type_arguments_instantiated);
// Instantiate non-null type arguments.
// In the non-factory case, we rely on the allocation stub to
// instantiate the type arguments.
@@ -1886,7 +2014,8 @@
// the type arguments and do not pass the instantiator.
ASSERT(type_arguments().IsRawInstantiatedRaw(type_arguments().Length()));
Label instantiator_not_null;
- __ bne(instantiator_reg, NULLREG, &instantiator_not_null);
+ __ BranchNotEqual(instantiator_reg, reinterpret_cast<int32_t>(Object::null()),
+ &instantiator_not_null);
// Null was used in VisitExtractConstructorTypeArguments as the
// instantiated type arguments, no proper instantiator needed.
__ LoadImmediate(instantiator_reg,
@@ -2067,7 +2196,9 @@
const intptr_t kCountLimit = 0x1F;
const intptr_t value = Smi::Cast(constant).Value();
if (value == 0) {
- // No code needed.
+ if (result != left) {
+ __ mov(result, left);
+ }
} else if ((value < 0) || (value >= kCountLimit)) {
// This condition may not be known earlier in some cases because
// of constant propagation, inlining, etc.
@@ -2101,6 +2232,7 @@
const intptr_t left_int = Smi::Cast(obj).Value();
if (left_int == 0) {
__ bltz(right, deopt);
+ __ mov(result, ZR);
return;
}
const intptr_t max_right = kSmiBits - Utils::HighestBit(left_int);
@@ -2163,24 +2295,33 @@
LocationSummary* BinarySmiOpInstr::MakeLocationSummary() const {
const intptr_t kNumInputs = 2;
+ const intptr_t kNumTemps = op_kind() == Token::kADD ? 1 : 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
if (op_kind() == Token::kTRUNCDIV) {
- UNIMPLEMENTED();
- return NULL;
- } else {
- const intptr_t kNumTemps = op_kind() == Token::kADD ? 1 : 0;
- LocationSummary* summary =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
- summary->set_in(0, Location::RequiresRegister());
- summary->set_in(1, Location::RegisterOrSmiConstant(right()));
- if (op_kind() == Token::kADD) {
- // Need an extra temp for the overflow detection code.
- summary->set_temp(0, Location::RequiresRegister());
+ if (RightIsPowerOfTwoConstant()) {
+ summary->set_in(0, Location::RequiresRegister());
+ ConstantInstr* right_constant = right()->definition()->AsConstant();
+ summary->set_in(1, Location::Constant(right_constant->value()));
+ summary->set_out(Location::RequiresRegister());
+ } else {
+ // Both inputs must be writable because they will be untagged.
+ summary->set_in(0, Location::WritableRegister());
+ summary->set_in(1, Location::WritableRegister());
+ summary->set_out(Location::RequiresRegister());
}
- // We make use of 3-operand instructions by not requiring result register
- // to be identical to first input register as on Intel.
- summary->set_out(Location::RequiresRegister());
return summary;
}
+ summary->set_in(0, Location::RequiresRegister());
+ summary->set_in(1, Location::RegisterOrSmiConstant(right()));
+ if (op_kind() == Token::kADD) {
+ // Need an extra temp for the overflow detection code.
+ summary->set_temp(0, Location::RequiresRegister());
+ }
+ // We make use of 3-operand instructions by not requiring result register
+ // to be identical to first input register as on Intel.
+ summary->set_out(Location::RequiresRegister());
+ return summary;
}
@@ -2251,7 +2392,33 @@
break;
}
case Token::kTRUNCDIV: {
- UNIMPLEMENTED();
+ const intptr_t value = Smi::Cast(constant).Value();
+ if (value == 1) {
+ if (result != left) {
+ __ mov(result, left);
+ }
+ break;
+ } else if (value == -1) {
+ // Check the corner case of dividing the 'MIN_SMI' with -1, in which
+ // case we cannot negate the result.
+ __ BranchEqual(left, 0x80000000, deopt);
+ __ subu(result, ZR, left);
+ break;
+ }
+ ASSERT((value != 0) && Utils::IsPowerOfTwo(Utils::Abs(value)));
+ const intptr_t shift_count =
+ Utils::ShiftForPowerOfTwo(Utils::Abs(value)) + kSmiTagSize;
+ ASSERT(kSmiTagSize == 1);
+ __ sra(TMP, left, 31);
+ ASSERT(shift_count > 1); // 1, -1 case handled above.
+ __ sll(TMP, TMP, 32 - shift_count);
+ __ addu(left, left, TMP);
+ ASSERT(shift_count > 0);
+ __ sra(result, left, shift_count);
+ if (value < 0) {
+ __ subu(result, ZR, result);
+ }
+ __ SmiTag(result);
break;
}
case Token::kBIT_AND: {
@@ -2293,7 +2460,9 @@
if (value == 0) {
// TODO(vegorov): should be handled outside.
- __ break_(0);
+ if (result != left) {
+ __ mov(result, left);
+ }
break;
} else if (value < 0) {
// TODO(vegorov): should be handled outside.
@@ -2366,7 +2535,16 @@
break;
}
case Token::kTRUNCDIV: {
- UNIMPLEMENTED();
+ // Handle divide by zero in runtime.
+ __ beq(right, ZR, deopt);
+ __ SmiUntag(left);
+ __ SmiUntag(right);
+ __ div(left, right);
+ __ mflo(result);
+ // Check the corner case of dividing the 'MIN_SMI' with -1, in which
+ // case we cannot tag the result.
+ __ BranchEqual(V0, 0x40000000, deopt);
+ __ SmiTag(result);
break;
}
case Token::kSHR: {
@@ -2399,35 +2577,142 @@
LocationSummary* CheckEitherNonSmiInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ intptr_t left_cid = left()->Type()->ToCid();
+ intptr_t right_cid = right()->Type()->ToCid();
+ ASSERT((left_cid != kDoubleCid) && (right_cid != kDoubleCid));
+ const intptr_t kNumInputs = 2;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, Location::RequiresRegister());
+ summary->set_in(1, Location::RequiresRegister());
+ return summary;
}
void CheckEitherNonSmiInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ Label* deopt = compiler->AddDeoptStub(deopt_id(), kDeoptBinaryDoubleOp);
+ intptr_t left_cid = left()->Type()->ToCid();
+ intptr_t right_cid = right()->Type()->ToCid();
+ Register left = locs()->in(0).reg();
+ Register right = locs()->in(1).reg();
+ if (left_cid == kSmiCid) {
+ __ andi(CMPRES, right, Immediate(kSmiTagMask));
+ } else if (right_cid == kSmiCid) {
+ __ andi(CMPRES, left, Immediate(kSmiTagMask));
+ } else {
+ __ or_(TMP, left, right);
+ __ andi(CMPRES, TMP, Immediate(kSmiTagMask));
+ }
+ __ beq(CMPRES, ZR, deopt);
}
LocationSummary* BoxDoubleInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ const intptr_t kNumInputs = 1;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs,
+ kNumTemps,
+ LocationSummary::kCallOnSlowPath);
+ summary->set_in(0, Location::RequiresFpuRegister());
+ summary->set_out(Location::RequiresRegister());
+ return summary;
}
+class BoxDoubleSlowPath : public SlowPathCode {
+ public:
+ explicit BoxDoubleSlowPath(BoxDoubleInstr* instruction)
+ : instruction_(instruction) { }
+
+ virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
+ __ Comment("BoxDoubleSlowPath");
+ __ Bind(entry_label());
+ const Class& double_class = compiler->double_class();
+ const Code& stub =
+ Code::Handle(StubCode::GetAllocationStubForClass(double_class));
+ const ExternalLabel label(double_class.ToCString(), stub.EntryPoint());
+
+ LocationSummary* locs = instruction_->locs();
+ locs->live_registers()->Remove(locs->out());
+
+ compiler->SaveLiveRegisters(locs);
+ compiler->GenerateCall(Scanner::kDummyTokenIndex, // No token position.
+ &label,
+ PcDescriptors::kOther,
+ locs);
+ if (locs->out().reg() != V0) {
+ __ mov(locs->out().reg(), V0);
+ }
+ compiler->RestoreLiveRegisters(locs);
+
+ __ b(exit_label());
+ }
+
+ private:
+ BoxDoubleInstr* instruction_;
+};
+
+
void BoxDoubleInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ BoxDoubleSlowPath* slow_path = new BoxDoubleSlowPath(this);
+ compiler->AddSlowPathCode(slow_path);
+
+ Register out_reg = locs()->out().reg();
+ DRegister value = locs()->in(0).fpu_reg();
+
+ __ TryAllocate(compiler->double_class(),
+ slow_path->entry_label(),
+ out_reg);
+ __ Bind(slow_path->exit_label());
+ __ StoreDToOffset(value, out_reg, Double::value_offset() - kHeapObjectTag);
}
LocationSummary* UnboxDoubleInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ const intptr_t kNumInputs = 1;
+ const intptr_t value_cid = value()->Type()->ToCid();
+ const bool needs_writable_input = (value_cid == kSmiCid);
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, needs_writable_input
+ ? Location::WritableRegister()
+ : Location::RequiresRegister());
+ summary->set_out(Location::RequiresFpuRegister());
+ return summary;
}
void UnboxDoubleInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ const intptr_t value_cid = value()->Type()->ToCid();
+ const Register value = locs()->in(0).reg();
+ const DRegister result = locs()->out().fpu_reg();
+
+ if (value_cid == kDoubleCid) {
+ __ LoadDFromOffset(result, value, Double::value_offset() - kHeapObjectTag);
+ } else if (value_cid == kSmiCid) {
+ __ SmiUntag(value); // Untag input before conversion.
+ __ mtc1(value, STMP1);
+ __ cvtdw(result, STMP1);
+ } else {
+ Label* deopt = compiler->AddDeoptStub(deopt_id_, kDeoptBinaryDoubleOp);
+ Label is_smi, done;
+
+ __ andi(CMPRES, value, Immediate(kSmiTagMask));
+ __ beq(CMPRES, ZR, &is_smi);
+ __ LoadClassId(TMP, value);
+ __ BranchNotEqual(TMP, kDoubleCid, deopt);
+ __ LoadDFromOffset(result, value, Double::value_offset() - kHeapObjectTag);
+ __ b(&done);
+ __ Bind(&is_smi);
+ // TODO(regis): Why do we preserve value here but not above?
+ __ sra(TMP, value, 1);
+ __ mtc1(TMP, STMP1);
+ __ cvtdw(result, STMP1);
+ __ Bind(&done);
+ }
}
@@ -2476,13 +2761,28 @@
LocationSummary* BinaryDoubleOpInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ const intptr_t kNumInputs = 2;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, Location::RequiresFpuRegister());
+ summary->set_in(1, Location::RequiresFpuRegister());
+ summary->set_out(Location::RequiresFpuRegister());
+ return summary;
}
void BinaryDoubleOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ DRegister left = locs()->in(0).fpu_reg();
+ DRegister right = locs()->in(1).fpu_reg();
+ DRegister result = locs()->out().fpu_reg();
+ switch (op_kind()) {
+ case Token::kADD: __ addd(result, left, right); break;
+ case Token::kSUB: __ subd(result, left, right); break;
+ case Token::kMUL: __ muld(result, left, right); break;
+ case Token::kDIV: __ divd(result, left, right); break;
+ default: UNREACHABLE();
+ }
}
@@ -2707,24 +3007,56 @@
LocationSummary* UnarySmiOpInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ const intptr_t kNumInputs = 1;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, Location::RequiresRegister());
+ // We make use of 3-operand instructions by not requiring result register
+ // to be identical to first input register as on Intel.
+ summary->set_out(Location::RequiresRegister());
+ return summary;
}
void UnarySmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ Register value = locs()->in(0).reg();
+ Register result = locs()->out().reg();
+ switch (op_kind()) {
+ case Token::kNEGATE: {
+ Label* deopt = compiler->AddDeoptStub(deopt_id(),
+ kDeoptUnaryOp);
+ __ SubuDetectOverflow(result, ZR, value, CMPRES);
+ __ bltz(CMPRES, deopt);
+ break;
+ }
+ case Token::kBIT_NOT:
+ __ nor(result, value, ZR);
+ __ addiu(result, result, Immediate(-1)); // Remove inverted smi-tag.
+ break;
+ default:
+ UNREACHABLE();
+ }
}
LocationSummary* SmiToDoubleInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ const intptr_t kNumInputs = 1;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* result =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
+ result->set_in(0, Location::WritableRegister());
+ result->set_out(Location::RequiresFpuRegister());
+ return result;
}
void SmiToDoubleInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ Register value = locs()->in(0).reg();
+ FpuRegister result = locs()->out().fpu_reg();
+ __ SmiUntag(value);
+ __ mtc1(value, STMP1);
+ __ cvtdw(result, STMP1);
}
@@ -2762,13 +3094,34 @@
LocationSummary* InvokeMathCFunctionInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ ASSERT((InputCount() == 1) || (InputCount() == 2));
+ const intptr_t kNumTemps = 0;
+ LocationSummary* result =
+ new LocationSummary(InputCount(), kNumTemps, LocationSummary::kCall);
+ result->set_in(0, Location::FpuRegisterLocation(D6));
+ if (InputCount() == 2) {
+ result->set_in(1, Location::FpuRegisterLocation(D7));
+ }
+ result->set_out(Location::FpuRegisterLocation(D0));
+ return result;
}
void InvokeMathCFunctionInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ // For pow-function return NAN if exponent is NAN.
+ Label do_call, skip_call;
+ if (recognized_kind() == MethodRecognizer::kDoublePow) {
+ DRegister exp = locs()->in(1).fpu_reg();
+ __ cund(exp, exp);
+ __ bc1f(&do_call);
+ // Exponent is NaN, return NaN.
+ __ movd(locs()->out().fpu_reg(), exp);
+ __ b(&skip_call);
+ }
+ __ Bind(&do_call);
+ // double values are passed and returned in vfp registers.
+ __ CallRuntime(TargetFunction());
+ __ Bind(&skip_call);
}
@@ -2844,7 +3197,8 @@
if (null_check()) {
Label* deopt = compiler->AddDeoptStub(deopt_id(),
kDeoptCheckClass);
- __ beq(locs()->in(0).reg(), NULLREG, deopt);
+ __ BranchEqual(locs()->in(0).reg(),
+ reinterpret_cast<int32_t>(Object::null()), deopt);
return;
}
@@ -3131,6 +3485,7 @@
// Special code for numbers (compare values instead of references.)
void StrictCompareInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ TraceSimMsg("StrictCompareInstr");
+ __ Comment("StrictCompareInstr");
ASSERT(kind() == Token::kEQ_STRICT || kind() == Token::kNE_STRICT);
Location left = locs()->in(0);
Location right = locs()->in(1);
@@ -3281,6 +3636,7 @@
void AllocateObjectInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ TraceSimMsg("AllocateObjectInstr");
+ __ Comment("AllocateObjectInstr");
const Class& cls = Class::ZoneHandle(constructor().Owner());
const Code& stub = Code::Handle(StubCode::GetAllocationStubForClass(cls));
const ExternalLabel label(cls.ToCString(), stub.EntryPoint());
@@ -3298,6 +3654,7 @@
void CreateClosureInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ __ Comment("CreateClosureInstr");
const Function& closure_function = function();
ASSERT(!closure_function.IsImplicitStaticClosureFunction());
const Code& stub = Code::Handle(
diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc
index 357c907..a55f175 100644
--- a/runtime/vm/intermediate_language_x64.cc
+++ b/runtime/vm/intermediate_language_x64.cc
@@ -24,6 +24,7 @@
DECLARE_FLAG(int, optimization_counter_threshold);
DECLARE_FLAG(bool, propagate_ic_data);
DECLARE_FLAG(bool, throw_on_javascript_int_overflow);
+DECLARE_FLAG(bool, use_osr);
// Generic summary for call instructions that have all arguments pushed
// on the stack and return the result in a fixed register RAX.
@@ -2105,6 +2106,13 @@
instruction_->deopt_id(),
kStackOverflowRuntimeEntry,
instruction_->locs());
+
+ if (FLAG_use_osr && !compiler->is_optimizing() && instruction_->in_loop()) {
+ // In unoptimized code, record loop stack checks as possible OSR entries.
+ compiler->AddCurrentDescriptor(PcDescriptors::kOsrEntry,
+ instruction_->deopt_id(),
+ 0); // No token position.
+ }
compiler->pending_deoptimization_env_ = NULL;
compiler->RestoreLiveRegisters(instruction_->locs());
__ jmp(exit_label());
@@ -2124,6 +2132,14 @@
__ movq(temp, Immediate(Isolate::Current()->stack_limit_address()));
__ cmpq(RSP, Address(temp, 0));
__ j(BELOW_EQUAL, slow_path->entry_label());
+ if (FLAG_use_osr && !compiler->is_optimizing() && in_loop()) {
+ // In unoptimized code check the usage counter to trigger OSR at loop
+ // stack checks.
+ __ LoadObject(temp, compiler->parsed_function().function());
+ __ cmpq(FieldAddress(temp, Function::usage_counter_offset()),
+ Immediate(2 * FLAG_optimization_counter_threshold));
+ __ j(GREATER_EQUAL, slow_path->entry_label());
+ }
__ Bind(slow_path->exit_label());
}
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index 9ba29ff..4f8588b 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -109,19 +109,20 @@
FieldAddress(T0, Array::length_offset()),
T2);
+ __ LoadImmediate(T7, reinterpret_cast<int32_t>(Object::null()));
// Initialize all array elements to raw_null.
// T0: new object start as a tagged pointer.
// T1: new object end address.
// T2: iterator which initially points to the start of the variable
// data area to be initialized.
- // NULLREG: null
+ // T7: null
__ AddImmediate(T2, T0, sizeof(RawArray) - kHeapObjectTag);
Label done;
Label init_loop;
__ Bind(&init_loop);
__ BranchUnsignedGreaterEqual(T2, T1, &done);
- __ sw(NULLREG, Address(T2, 0));
+ __ sw(T7, Address(T2, 0));
__ b(&init_loop);
__ delay_slot()->addiu(T2, T2, Immediate(kWordSize));
__ Bind(&done);
@@ -201,13 +202,14 @@
__ lw(T2, Address(SP, 0 * kWordSize)); // Value.
// Null value is valid for any type.
- __ beq(T2, NULLREG, &checked_ok);
+ __ LoadImmediate(T7, reinterpret_cast<int32_t>(Object::null()));
+ __ beq(T2, T7, &checked_ok);
__ delay_slot()->lw(T1, Address(SP, 2 * kWordSize)); // Array.
__ lw(T1, FieldAddress(T1, type_args_field_offset));
// T1: Type arguments of array.
- __ beq(T1, NULLREG, &checked_ok);
+ __ beq(T1, T7, &checked_ok);
// Check if it's dynamic.
// For now handle only TypeArguments and bail out if InstantiatedTypeArgs.
@@ -462,8 +464,9 @@
__ StoreIntoObject(T2,
FieldAddress(T1, Array::data_offset()),
T0);
+ __ LoadImmediate(T7, reinterpret_cast<int32_t>(Object::null()));
__ Ret();
- __ delay_slot()->mov(V0, NULLREG);
+ __ delay_slot()->mov(V0, T7);
__ Bind(&fall_through);
return false;
}
@@ -836,7 +839,6 @@
bool Intrinsifier::Integer_bitXorFromInteger(Assembler* assembler) {
Label fall_through;
- __ Untested("Intrinsifier::Integer_bitXorFromInteger");
TestBothArgumentsSmis(assembler, &fall_through); // Checks two smis.
__ Ret();
@@ -1138,24 +1140,15 @@
// returns false. Any non-double arg1 causes control flow to fall through to the
// slow case (compiled method body).
static bool CompareDoubles(Assembler* assembler, Condition true_condition) {
- Label is_smi, no_conversion, no_NaN, fall_through;
+ Label is_smi, double_op, no_NaN, fall_through;
+ __ Comment("CompareDoubles Intrinsic");
TestLastArgumentIsDouble(assembler, &is_smi, &fall_through);
// Both arguments are double, right operand is in T0.
- __ lwc1(F2, FieldAddress(T0, Double::value_offset()));
- __ b(&no_conversion);
- __ delay_slot()->lwc1(F3,
- FieldAddress(T0, Double::value_offset() + kWordSize));
-
- __ Bind(&is_smi);
- __ SmiUntag(T0);
- __ mtc1(T0, F4);
- __ cvtdw(D1, F4);
-
- __ Bind(&no_conversion);
+ __ LoadDFromOffset(D1, T0, Double::value_offset() - kHeapObjectTag);
+ __ Bind(&double_op);
__ lw(T0, Address(SP, 1 * kWordSize)); // Left argument.
- __ lwc1(F0, FieldAddress(T0, Double::value_offset()));
- __ lwc1(F1, FieldAddress(T0, Double::value_offset() + kWordSize));
+ __ LoadDFromOffset(D0, T0, Double::value_offset() - kHeapObjectTag);
// Now, left is in D0, right is in D1.
__ cund(D0, D1); // Check for NaN.
@@ -1165,11 +1158,11 @@
__ Bind(&no_NaN);
switch (true_condition) {
- case EQ: __ ceqd(D1, D0); break;
- case LT: __ coltd(D1, D0); break;
- case LE: __ coled(D1, D0); break;
- case GT: __ coltd(D0, D1); break;
- case GE: __ coled(D0, D1); break;
+ case EQ: __ ceqd(D0, D1); break;
+ case LT: __ coltd(D0, D1); break;
+ case LE: __ coled(D0, D1); break;
+ case GT: __ coltd(D1, D0); break;
+ case GE: __ coled(D1, D0); break;
default: {
// Only passing the above conditions to this function.
UNREACHABLE();
@@ -1184,6 +1177,14 @@
__ Bind(&is_true);
__ LoadObject(V0, Bool::True());
__ Ret();
+
+
+ __ Bind(&is_smi);
+ __ SmiUntag(T0);
+ __ mtc1(T0, STMP1);
+ __ cvtdw(D1, STMP1);
+ __ b(&double_op);
+
__ Bind(&fall_through);
return false;
}
@@ -1268,7 +1269,6 @@
// Left is double right is integer (Bigint, Mint or Smi)
bool Intrinsifier::Double_mulFromInteger(Assembler* assembler) {
Label fall_through;
- __ Untested("Intrinsifier::Double_mulFromInteger");
// Only Smi-s allowed.
__ lw(T0, Address(SP, 0 * kWordSize));
__ andi(CMPRES, T0, Immediate(kSmiTagMask));
@@ -1320,7 +1320,7 @@
bool Intrinsifier::Double_getIsNaN(Assembler* assembler) {
Label is_true;
- __ Untested("Intrinsifier::Double_getIsNaN");
+
__ lw(T0, Address(SP, 0 * kWordSize));
__ lwc1(F0, FieldAddress(T0, Double::value_offset()));
__ lwc1(F1, FieldAddress(T0, Double::value_offset() + kWordSize));
@@ -1337,7 +1337,6 @@
bool Intrinsifier::Double_getIsNegative(Assembler* assembler) {
Label is_false, is_true, is_zero;
- __ Untested("Intrinsifier::Double_getIsNegative");
__ lw(T0, Address(SP, 0 * kWordSize));
__ lwc1(F0, FieldAddress(T0, Double::value_offset()));
__ lwc1(F1, FieldAddress(T0, Double::value_offset() + kWordSize));
@@ -1390,11 +1389,9 @@
bool Intrinsifier::Math_sqrt(Assembler* assembler) {
Label fall_through, is_smi, double_op;
- __ Untested("Intrinsifier::Math_sqrt");
TestLastArgumentIsDouble(assembler, &is_smi, &fall_through);
// Argument is double and is in T0.
- __ lwc1(F0, FieldAddress(T0, Double::value_offset()));
- __ lwc1(F1, FieldAddress(T0, Double::value_offset() + kWordSize));
+ __ LoadDFromOffset(D1, T0, Double::value_offset() - kHeapObjectTag);
__ Bind(&double_op);
__ sqrtd(D0, D1);
const Class& double_class = Class::Handle(
@@ -1447,8 +1444,6 @@
ASSERT(Utils::IsUint(32, a_int_value));
int32_t a_int32_value = static_cast<int32_t>(a_int_value);
- __ Untested("Random_nextState");
-
__ lw(T0, Address(SP, 0 * kWordSize)); // Receiver.
__ lw(T1, FieldAddress(T0, state_field.Offset())); // Field '_state'.
diff --git a/runtime/vm/mirrors_api_impl.cc b/runtime/vm/mirrors_api_impl.cc
new file mode 100644
index 0000000..c79d3ce
--- /dev/null
+++ b/runtime/vm/mirrors_api_impl.cc
@@ -0,0 +1,869 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#include "include/dart_mirrors_api.h"
+
+#include "platform/assert.h"
+#include "vm/class_finalizer.h"
+#include "vm/dart.h"
+#include "vm/dart_api_impl.h"
+#include "vm/dart_api_state.h"
+#include "vm/dart_entry.h"
+#include "vm/exceptions.h"
+#include "vm/growable_array.h"
+#include "vm/object.h"
+#include "vm/resolver.h"
+#include "vm/stack_frame.h"
+#include "vm/symbols.h"
+
+namespace dart {
+
+// When we want to return a handle to a type to the user, we handle
+// class-types differently than some other types.
+static Dart_Handle TypeToHandle(Isolate* isolate,
+ const char* function_name,
+ const AbstractType& type) {
+ if (type.IsMalformed()) {
+ const Error& error = Error::Handle(type.malformed_error());
+ return Api::NewError("%s: malformed type encountered: %s.",
+ function_name, error.ToErrorCString());
+ } else if (type.HasResolvedTypeClass()) {
+ const Class& cls = Class::Handle(isolate, type.type_class());
+#if defined(DEBUG)
+ const Library& lib = Library::Handle(cls.library());
+ if (lib.IsNull()) {
+ ASSERT(cls.IsDynamicClass() || cls.IsVoidClass());
+ }
+#endif
+ return Api::NewHandle(isolate, cls.raw());
+ } else if (type.IsTypeParameter()) {
+ return Api::NewHandle(isolate, type.raw());
+ } else {
+ return Api::NewError("%s: unexpected type '%s' encountered.",
+ function_name, type.ToCString());
+ }
+}
+
+
+// --- Classes and Interfaces Reflection ---
+
+DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle clazz) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
+ if (cls.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, clazz, Class);
+ }
+ return Api::NewHandle(isolate, cls.UserVisibleName());
+}
+
+
+DART_EXPORT Dart_Handle Dart_ClassGetLibrary(Dart_Handle clazz) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
+ if (cls.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, clazz, Class);
+ }
+
+#if defined(DEBUG)
+ const Library& lib = Library::Handle(cls.library());
+ if (lib.IsNull()) {
+ // ASSERT(cls.IsDynamicClass() || cls.IsVoidClass());
+ if (!cls.IsDynamicClass() && !cls.IsVoidClass()) {
+ fprintf(stderr, "NO LIBRARY: %s\n", cls.ToCString());
+ }
+ }
+#endif
+
+ return Api::NewHandle(isolate, cls.library());
+}
+
+
+DART_EXPORT Dart_Handle Dart_ClassGetInterfaceCount(Dart_Handle clazz,
+ intptr_t* count) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
+ if (cls.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, clazz, Class);
+ }
+
+ const Array& interface_types = Array::Handle(isolate, cls.interfaces());
+ if (interface_types.IsNull()) {
+ *count = 0;
+ } else {
+ *count = interface_types.Length();
+ }
+ return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_ClassGetInterfaceAt(Dart_Handle clazz,
+ intptr_t index) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
+ if (cls.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, clazz, Class);
+ }
+
+ // Finalize all classes.
+ Dart_Handle state = Api::CheckIsolateState(isolate);
+ if (::Dart_IsError(state)) {
+ return state;
+ }
+
+ const Array& interface_types = Array::Handle(isolate, cls.interfaces());
+ if (index < 0 || index >= interface_types.Length()) {
+ return Api::NewError("%s: argument 'index' out of bounds.", CURRENT_FUNC);
+ }
+ Type& interface_type = Type::Handle(isolate);
+ interface_type ^= interface_types.At(index);
+ if (interface_type.HasResolvedTypeClass()) {
+ return Api::NewHandle(isolate, interface_type.type_class());
+ }
+ const String& type_name =
+ String::Handle(isolate, interface_type.TypeClassName());
+ return Api::NewError("%s: internal error: found unresolved type class '%s'.",
+ CURRENT_FUNC, type_name.ToCString());
+}
+
+
+DART_EXPORT bool Dart_ClassIsTypedef(Dart_Handle clazz) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
+ if (cls.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, clazz, Class);
+ }
+ // For now we represent typedefs as non-canonical signature classes.
+ // I anticipate this may change if we make typedefs more general.
+ return cls.IsSignatureClass() && !cls.IsCanonicalSignatureClass();
+}
+
+
+DART_EXPORT Dart_Handle Dart_ClassGetTypedefReferent(Dart_Handle clazz) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
+ if (cls.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, clazz, Class);
+ }
+
+ if (!cls.IsSignatureClass() && !cls.IsCanonicalSignatureClass()) {
+ const String& cls_name = String::Handle(cls.UserVisibleName());
+ return Api::NewError("%s: class '%s' is not a typedef class. "
+ "See Dart_ClassIsTypedef.",
+ CURRENT_FUNC, cls_name.ToCString());
+ }
+
+ const Function& func = Function::Handle(isolate, cls.signature_function());
+ return Api::NewHandle(isolate, func.signature_class());
+}
+
+
+DART_EXPORT bool Dart_ClassIsFunctionType(Dart_Handle clazz) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
+ if (cls.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, clazz, Class);
+ }
+ // A class represents a function type when it is a canonical
+ // signature class.
+ return cls.IsCanonicalSignatureClass();
+}
+
+
+DART_EXPORT Dart_Handle Dart_ClassGetFunctionTypeSignature(Dart_Handle clazz) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
+ if (cls.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, clazz, Class);
+ }
+ if (!cls.IsCanonicalSignatureClass()) {
+ const String& cls_name = String::Handle(cls.UserVisibleName());
+ return Api::NewError("%s: class '%s' is not a function-type class. "
+ "See Dart_ClassIsFunctionType.",
+ CURRENT_FUNC, cls_name.ToCString());
+ }
+ return Api::NewHandle(isolate, cls.signature_function());
+}
+
+
+// --- Function and Variable Reflection ---
+
+// Outside of the vm, we expose setter names with a trailing '='.
+static bool HasExternalSetterSuffix(const String& name) {
+ return name.CharAt(name.Length() - 1) == '=';
+}
+
+
+static RawString* RemoveExternalSetterSuffix(const String& name) {
+ ASSERT(HasExternalSetterSuffix(name));
+ return String::SubString(name, 0, name.Length() - 1);
+}
+
+
+DART_EXPORT Dart_Handle Dart_GetFunctionNames(Dart_Handle target) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target));
+ if (obj.IsError()) {
+ return target;
+ }
+
+ const GrowableObjectArray& names =
+ GrowableObjectArray::Handle(isolate, GrowableObjectArray::New());
+ Function& func = Function::Handle();
+ String& name = String::Handle();
+
+ if (obj.IsClass()) {
+ const Class& cls = Class::Cast(obj);
+ const Error& error = Error::Handle(isolate, cls.EnsureIsFinalized(isolate));
+ if (!error.IsNull()) {
+ return Api::NewHandle(isolate, error.raw());
+ }
+ const Array& func_array = Array::Handle(cls.functions());
+
+ // Some special types like 'dynamic' have a null functions list.
+ if (!func_array.IsNull()) {
+ for (intptr_t i = 0; i < func_array.Length(); ++i) {
+ func ^= func_array.At(i);
+
+ // Skip implicit getters and setters.
+ if (func.kind() == RawFunction::kImplicitGetter ||
+ func.kind() == RawFunction::kImplicitSetter ||
+ func.kind() == RawFunction::kConstImplicitGetter ||
+ func.kind() == RawFunction::kMethodExtractor) {
+ continue;
+ }
+
+ name = func.UserVisibleName();
+ names.Add(name);
+ }
+ }
+ } else if (obj.IsLibrary()) {
+ const Library& lib = Library::Cast(obj);
+ DictionaryIterator it(lib);
+ Object& obj = Object::Handle();
+ while (it.HasNext()) {
+ obj = it.GetNext();
+ if (obj.IsFunction()) {
+ func ^= obj.raw();
+ name = func.UserVisibleName();
+ names.Add(name);
+ }
+ }
+ } else {
+ return Api::NewError(
+ "%s expects argument 'target' to be a class or library.",
+ CURRENT_FUNC);
+ }
+ return Api::NewHandle(isolate, Array::MakeArray(names));
+}
+
+
+DART_EXPORT Dart_Handle Dart_LookupFunction(Dart_Handle target,
+ Dart_Handle function_name) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target));
+ if (obj.IsError()) {
+ return target;
+ }
+ const String& func_name = Api::UnwrapStringHandle(isolate, function_name);
+ if (func_name.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function_name, String);
+ }
+
+ Function& func = Function::Handle(isolate);
+ String& tmp_name = String::Handle(isolate);
+ if (obj.IsClass()) {
+ const Class& cls = Class::Cast(obj);
+
+ // Case 1. Lookup the unmodified function name.
+ func = cls.LookupFunctionAllowPrivate(func_name);
+
+ // Case 2. Lookup the function without the external setter suffix
+ // '='. Make sure to do this check after the regular lookup, so
+ // that we don't interfere with operator lookups (like ==).
+ if (func.IsNull() && HasExternalSetterSuffix(func_name)) {
+ tmp_name = RemoveExternalSetterSuffix(func_name);
+ tmp_name = Field::SetterName(tmp_name);
+ func = cls.LookupFunctionAllowPrivate(tmp_name);
+ }
+
+ // Case 3. Lookup the funciton with the getter prefix prepended.
+ if (func.IsNull()) {
+ tmp_name = Field::GetterName(func_name);
+ func = cls.LookupFunctionAllowPrivate(tmp_name);
+ }
+
+ // Case 4. Lookup the function with a . appended to find the
+ // unnamed constructor.
+ if (func.IsNull()) {
+ tmp_name = String::Concat(func_name, Symbols::Dot());
+ func = cls.LookupFunctionAllowPrivate(tmp_name);
+ }
+ } else if (obj.IsLibrary()) {
+ const Library& lib = Library::Cast(obj);
+
+ // Case 1. Lookup the unmodified function name.
+ func = lib.LookupFunctionAllowPrivate(func_name);
+
+ // Case 2. Lookup the function without the external setter suffix
+ // '='. Make sure to do this check after the regular lookup, so
+ // that we don't interfere with operator lookups (like ==).
+ if (func.IsNull() && HasExternalSetterSuffix(func_name)) {
+ tmp_name = RemoveExternalSetterSuffix(func_name);
+ tmp_name = Field::SetterName(tmp_name);
+ func = lib.LookupFunctionAllowPrivate(tmp_name);
+ }
+
+ // Case 3. Lookup the function with the getter prefix prepended.
+ if (func.IsNull()) {
+ tmp_name = Field::GetterName(func_name);
+ func = lib.LookupFunctionAllowPrivate(tmp_name);
+ }
+ } else {
+ return Api::NewError(
+ "%s expects argument 'target' to be a class or library.",
+ CURRENT_FUNC);
+ }
+
+#if defined(DEBUG)
+ if (!func.IsNull()) {
+ // We only provide access to a subset of function kinds.
+ RawFunction::Kind func_kind = func.kind();
+ ASSERT(func_kind == RawFunction::kRegularFunction ||
+ func_kind == RawFunction::kGetterFunction ||
+ func_kind == RawFunction::kSetterFunction ||
+ func_kind == RawFunction::kConstructor);
+ }
+#endif
+ return Api::NewHandle(isolate, func.raw());
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ return Api::NewHandle(isolate, func.UserVisibleName());
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionOwner(Dart_Handle function) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ if (func.IsNonImplicitClosureFunction()) {
+ RawFunction* parent_function = func.parent_function();
+ return Api::NewHandle(isolate, parent_function);
+ }
+ const Class& owner = Class::Handle(func.Owner());
+ ASSERT(!owner.IsNull());
+ if (owner.IsTopLevel()) {
+ // Top-level functions are implemented as members of a hidden class. We hide
+ // that class here and instead answer the library.
+#if defined(DEBUG)
+ const Library& lib = Library::Handle(owner.library());
+ if (lib.IsNull()) {
+ ASSERT(owner.IsDynamicClass() || owner.IsVoidClass());
+ }
+#endif
+ return Api::NewHandle(isolate, owner.library());
+ } else {
+ return Api::NewHandle(isolate, owner.raw());
+ }
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionIsAbstract(Dart_Handle function,
+ bool* is_abstract) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ if (is_abstract == NULL) {
+ RETURN_NULL_ERROR(is_abstract);
+ }
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ *is_abstract = func.is_abstract();
+ return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function,
+ bool* is_static) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ if (is_static == NULL) {
+ RETURN_NULL_ERROR(is_static);
+ }
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ *is_static = func.is_static();
+ return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionIsConstructor(Dart_Handle function,
+ bool* is_constructor) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ if (is_constructor == NULL) {
+ RETURN_NULL_ERROR(is_constructor);
+ }
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ *is_constructor = func.kind() == RawFunction::kConstructor;
+ return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionIsGetter(Dart_Handle function,
+ bool* is_getter) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ if (is_getter == NULL) {
+ RETURN_NULL_ERROR(is_getter);
+ }
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ *is_getter = func.IsGetterFunction();
+ return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionIsSetter(Dart_Handle function,
+ bool* is_setter) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ if (is_setter == NULL) {
+ RETURN_NULL_ERROR(is_setter);
+ }
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ *is_setter = (func.kind() == RawFunction::kSetterFunction);
+ return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionReturnType(Dart_Handle function) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+
+ if (func.kind() == RawFunction::kConstructor) {
+ // Special case the return type for constructors. Inside the vm
+ // we mark them as returning dynamic, but for the purposes of
+ // reflection, they return the type of the class being
+ // constructed.
+ return Api::NewHandle(isolate, func.Owner());
+ } else {
+ const AbstractType& return_type =
+ AbstractType::Handle(isolate, func.result_type());
+ return TypeToHandle(isolate, "Dart_FunctionReturnType", return_type);
+ }
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionParameterCounts(
+ Dart_Handle function,
+ int64_t* fixed_param_count,
+ int64_t* opt_param_count) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ if (fixed_param_count == NULL) {
+ RETURN_NULL_ERROR(fixed_param_count);
+ }
+ if (opt_param_count == NULL) {
+ RETURN_NULL_ERROR(opt_param_count);
+ }
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+
+ // We hide implicit parameters, such as a method's receiver. This is
+ // consistent with Invoke or New, which don't expect their callers to
+ // provide them in the argument lists they are handed.
+ *fixed_param_count = func.num_fixed_parameters() -
+ func.NumImplicitParameters();
+ // TODO(regis): Separately report named and positional optional param counts.
+ *opt_param_count = func.NumOptionalParameters();
+
+ ASSERT(*fixed_param_count >= 0);
+ ASSERT(*opt_param_count >= 0);
+
+ return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionParameterType(Dart_Handle function,
+ int parameter_index) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+
+ const intptr_t num_implicit_params = func.NumImplicitParameters();
+ const intptr_t num_params = func.NumParameters() - num_implicit_params;
+ if (parameter_index < 0 || parameter_index >= num_params) {
+ return Api::NewError(
+ "%s: argument 'parameter_index' out of range. "
+ "Expected 0..%"Pd" but saw %d.",
+ CURRENT_FUNC, num_params, parameter_index);
+ }
+ const AbstractType& param_type =
+ AbstractType::Handle(isolate, func.ParameterTypeAt(
+ num_implicit_params + parameter_index));
+ return TypeToHandle(isolate, "Dart_FunctionParameterType", param_type);
+}
+
+
+DART_EXPORT Dart_Handle Dart_GetVariableNames(Dart_Handle target) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target));
+ if (obj.IsError()) {
+ return target;
+ }
+
+ const GrowableObjectArray& names =
+ GrowableObjectArray::Handle(isolate, GrowableObjectArray::New());
+ Field& field = Field::Handle(isolate);
+ String& name = String::Handle(isolate);
+
+ if (obj.IsClass()) {
+ const Class& cls = Class::Cast(obj);
+ const Error& error = Error::Handle(isolate, cls.EnsureIsFinalized(isolate));
+ if (!error.IsNull()) {
+ return Api::NewHandle(isolate, error.raw());
+ }
+ const Array& field_array = Array::Handle(cls.fields());
+
+ // Some special types like 'dynamic' have a null fields list.
+ //
+ // TODO(turnidge): Fix 'dynamic' so that it does not have a null
+ // fields list. This will have to wait until the empty array is
+ // allocated in the vm isolate.
+ if (!field_array.IsNull()) {
+ for (intptr_t i = 0; i < field_array.Length(); ++i) {
+ field ^= field_array.At(i);
+ name = field.UserVisibleName();
+ names.Add(name);
+ }
+ }
+ } else if (obj.IsLibrary()) {
+ const Library& lib = Library::Cast(obj);
+ DictionaryIterator it(lib);
+ Object& obj = Object::Handle(isolate);
+ while (it.HasNext()) {
+ obj = it.GetNext();
+ if (obj.IsField()) {
+ field ^= obj.raw();
+ name = field.UserVisibleName();
+ names.Add(name);
+ }
+ }
+ } else {
+ return Api::NewError(
+ "%s expects argument 'target' to be a class or library.",
+ CURRENT_FUNC);
+ }
+ return Api::NewHandle(isolate, Array::MakeArray(names));
+}
+
+
+DART_EXPORT Dart_Handle Dart_LookupVariable(Dart_Handle target,
+ Dart_Handle variable_name) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target));
+ if (obj.IsError()) {
+ return target;
+ }
+ const String& var_name = Api::UnwrapStringHandle(isolate, variable_name);
+ if (var_name.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, variable_name, String);
+ }
+ if (obj.IsClass()) {
+ const Class& cls = Class::Cast(obj);
+ return Api::NewHandle(isolate, cls.LookupField(var_name));
+ }
+ if (obj.IsLibrary()) {
+ const Library& lib = Library::Cast(obj);
+ return Api::NewHandle(isolate, lib.LookupFieldAllowPrivate(var_name));
+ }
+ return Api::NewError(
+ "%s expects argument 'target' to be a class or library.",
+ CURRENT_FUNC);
+}
+
+
+DART_EXPORT Dart_Handle Dart_VariableName(Dart_Handle variable) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Field& var = Api::UnwrapFieldHandle(isolate, variable);
+ if (var.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, variable, Field);
+ }
+ return Api::NewHandle(isolate, var.UserVisibleName());
+}
+
+
+DART_EXPORT Dart_Handle Dart_VariableIsStatic(Dart_Handle variable,
+ bool* is_static) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ if (is_static == NULL) {
+ RETURN_NULL_ERROR(is_static);
+ }
+ const Field& var = Api::UnwrapFieldHandle(isolate, variable);
+ if (var.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, variable, Field);
+ }
+ *is_static = var.is_static();
+ return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_VariableIsFinal(Dart_Handle variable,
+ bool* is_final) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ if (is_final == NULL) {
+ RETURN_NULL_ERROR(is_final);
+ }
+ const Field& var = Api::UnwrapFieldHandle(isolate, variable);
+ if (var.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, variable, Field);
+ }
+ *is_final = var.is_final();
+ return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_VariableType(Dart_Handle variable) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Field& var = Api::UnwrapFieldHandle(isolate, variable);
+ if (var.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, variable, Field);
+ }
+
+ const AbstractType& type = AbstractType::Handle(isolate, var.type());
+ return TypeToHandle(isolate, "Dart_VariableType", type);
+}
+
+
+DART_EXPORT Dart_Handle Dart_GetTypeVariableNames(Dart_Handle clazz) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
+ if (cls.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, clazz, Class);
+ }
+
+ const intptr_t num_type_params = cls.NumTypeParameters();
+ const TypeArguments& type_params =
+ TypeArguments::Handle(cls.type_parameters());
+
+ const GrowableObjectArray& names =
+ GrowableObjectArray::Handle(isolate, GrowableObjectArray::New());
+ TypeParameter& type_param = TypeParameter::Handle(isolate);
+ String& name = String::Handle(isolate);
+ for (intptr_t i = 0; i < num_type_params; i++) {
+ type_param ^= type_params.TypeAt(i);
+ name = type_param.name();
+ names.Add(name);
+ }
+ return Api::NewHandle(isolate, Array::MakeArray(names));
+}
+
+
+DART_EXPORT Dart_Handle Dart_LookupTypeVariable(
+ Dart_Handle clazz,
+ Dart_Handle type_variable_name) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Class& cls = Api::UnwrapClassHandle(isolate, clazz);
+ if (cls.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, clazz, Class);
+ }
+ const String& var_name = Api::UnwrapStringHandle(isolate, type_variable_name);
+ if (var_name.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, type_variable_name, String);
+ }
+
+ const intptr_t num_type_params = cls.NumTypeParameters();
+ const TypeArguments& type_params =
+ TypeArguments::Handle(cls.type_parameters());
+
+ TypeParameter& type_param = TypeParameter::Handle(isolate);
+ String& name = String::Handle(isolate);
+ for (intptr_t i = 0; i < num_type_params; i++) {
+ type_param ^= type_params.TypeAt(i);
+ name = type_param.name();
+ if (name.Equals(var_name)) {
+ return Api::NewHandle(isolate, type_param.raw());
+ }
+ }
+ const String& cls_name = String::Handle(cls.UserVisibleName());
+ return Api::NewError(
+ "%s: Could not find type variable named '%s' for class %s.\n",
+ CURRENT_FUNC, var_name.ToCString(), cls_name.ToCString());
+}
+
+
+DART_EXPORT Dart_Handle Dart_TypeVariableName(Dart_Handle type_variable) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const TypeParameter& type_var =
+ Api::UnwrapTypeParameterHandle(isolate, type_variable);
+ if (type_var.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, type_variable, TypeParameter);
+ }
+ return Api::NewHandle(isolate, type_var.name());
+}
+
+
+DART_EXPORT Dart_Handle Dart_TypeVariableOwner(Dart_Handle type_variable) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const TypeParameter& type_var =
+ Api::UnwrapTypeParameterHandle(isolate, type_variable);
+ if (type_var.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, type_variable, TypeParameter);
+ }
+ const Class& owner = Class::Handle(type_var.parameterized_class());
+ ASSERT(!owner.IsNull() && owner.IsClass());
+ return Api::NewHandle(isolate, owner.raw());
+}
+
+
+DART_EXPORT Dart_Handle Dart_TypeVariableUpperBound(Dart_Handle type_variable) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const TypeParameter& type_var =
+ Api::UnwrapTypeParameterHandle(isolate, type_variable);
+ if (type_var.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, type_variable, TypeParameter);
+ }
+ const AbstractType& bound = AbstractType::Handle(type_var.bound());
+ return TypeToHandle(isolate, "Dart_TypeVariableUpperBound", bound);
+}
+
+
+// --- Libraries Reflection ---
+
+DART_EXPORT Dart_Handle Dart_LibraryName(Dart_Handle library) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Library& lib = Api::UnwrapLibraryHandle(isolate, library);
+ if (lib.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, library, Library);
+ }
+ const String& name = String::Handle(isolate, lib.name());
+ ASSERT(!name.IsNull());
+ return Api::NewHandle(isolate, name.raw());
+}
+
+
+DART_EXPORT Dart_Handle Dart_LibraryGetClassNames(Dart_Handle library) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Library& lib = Api::UnwrapLibraryHandle(isolate, library);
+ if (lib.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, library, Library);
+ }
+
+ const GrowableObjectArray& names =
+ GrowableObjectArray::Handle(isolate, GrowableObjectArray::New());
+ ClassDictionaryIterator it(lib);
+ Class& cls = Class::Handle();
+ String& name = String::Handle();
+ while (it.HasNext()) {
+ cls = it.GetNextClass();
+ if (cls.IsSignatureClass()) {
+ if (!cls.IsCanonicalSignatureClass()) {
+ // This is a typedef. Add it to the list of class names.
+ name = cls.UserVisibleName();
+ names.Add(name);
+ } else {
+ // Skip canonical signature classes. These are not named.
+ }
+ } else {
+ name = cls.UserVisibleName();
+ names.Add(name);
+ }
+ }
+ return Api::NewHandle(isolate, Array::MakeArray(names));
+}
+
+
+// --- Closures Reflection ---
+
+DART_EXPORT Dart_Handle Dart_ClosureFunction(Dart_Handle closure) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Instance& closure_obj = Api::UnwrapInstanceHandle(isolate, closure);
+ if (closure_obj.IsNull() || !closure_obj.IsClosure()) {
+ RETURN_TYPE_ERROR(isolate, closure, Instance);
+ }
+
+ ASSERT(ClassFinalizer::AllClassesFinalized());
+
+ RawFunction* rf = Closure::function(closure_obj);
+ return Api::NewHandle(isolate, rf);
+}
+
+
+// --- Metadata Reflection ----
+
+DART_EXPORT Dart_Handle Dart_GetMetadata(Dart_Handle object) {
+ Isolate* isolate = Isolate::Current();
+ CHECK_ISOLATE(isolate);
+ DARTSCOPE(isolate);
+ const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(object));
+ Class& cls = Class::Handle(isolate);
+ if (obj.IsClass()) {
+ cls ^= obj.raw();
+ } else if (obj.IsFunction()) {
+ cls = Function::Cast(obj).origin();
+ } else if (obj.IsField()) {
+ cls = Field::Cast(obj).origin();
+ } else {
+ return Api::NewHandle(isolate, Object::empty_array().raw());
+ }
+ const Library& lib = Library::Handle(cls.library());
+ return Api::NewHandle(isolate, lib.GetMetadata(obj));
+}
+
+} // namespace dart
diff --git a/runtime/vm/native_api_impl.cc b/runtime/vm/native_api_impl.cc
new file mode 100644
index 0000000..a142540
--- /dev/null
+++ b/runtime/vm/native_api_impl.cc
@@ -0,0 +1,136 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#include "include/dart_native_api.h"
+
+#include "platform/assert.h"
+#include "vm/dart_api_impl.h"
+#include "vm/dart_api_message.h"
+#include "vm/dart_api_state.h"
+#include "vm/message.h"
+#include "vm/native_message_handler.h"
+#include "vm/port.h"
+
+namespace dart {
+
+// --- Message sending/receiving from native code ---
+
+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);
+}
+
+
+DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message) {
+ uint8_t* buffer = NULL;
+ ApiMessageWriter writer(&buffer, allocator);
+ bool success = writer.WriteCMessage(message);
+
+ if (!success) return success;
+
+ // Post the message at the given port.
+ return PortMap::PostMessage(new Message(
+ port_id, Message::kIllegalPort, buffer, writer.BytesWritten(),
+ Message::kNormalPriority));
+}
+
+
+DART_EXPORT Dart_Port Dart_NewNativePort(const char* name,
+ Dart_NativeMessageHandler handler,
+ bool handle_concurrently) {
+ if (name == NULL) {
+ name = "<UnnamedNativePort>";
+ }
+ if (handler == NULL) {
+ OS::PrintErr("%s expects argument 'handler' to be non-null.\n",
+ CURRENT_FUNC);
+ return ILLEGAL_PORT;
+ }
+ // Start the native port without a current isolate.
+ IsolateSaver saver(Isolate::Current());
+ Isolate::SetCurrent(NULL);
+
+ NativeMessageHandler* nmh = new NativeMessageHandler(name, handler);
+ Dart_Port port_id = PortMap::CreatePort(nmh);
+ nmh->Run(Dart::thread_pool(), NULL, NULL, 0);
+ return port_id;
+}
+
+
+DART_EXPORT bool Dart_CloseNativePort(Dart_Port native_port_id) {
+ // Close the native port without a current isolate.
+ IsolateSaver saver(Isolate::Current());
+ Isolate::SetCurrent(NULL);
+
+ // TODO(turnidge): Check that the port is native before trying to close.
+ return PortMap::ClosePort(native_port_id);
+}
+
+
+// --- Profiling support ---
+
+// TODO(7565): Dartium should use the new VM flag "generate_pprof_symbols" for
+// pprof profiling. Then these symbols should be removed.
+
+DART_EXPORT void Dart_InitPprofSupport() { }
+
+DART_EXPORT void Dart_GetPprofSymbolInfo(void** buffer, int* buffer_size) {
+ *buffer = NULL;
+ *buffer_size = 0;
+}
+
+
+// --- Heap Profiler ---
+
+DART_EXPORT Dart_Handle Dart_HeapProfile(Dart_FileWriteCallback callback,
+ void* stream) {
+ Isolate* isolate = Isolate::Current();
+ CHECK_ISOLATE(isolate);
+ if (callback == NULL) {
+ RETURN_NULL_ERROR(callback);
+ }
+ isolate->heap()->Profile(callback, stream);
+ return Api::Success();
+}
+
+
+// --- Verification tools ---
+
+static void CompileAll(Isolate* isolate, Dart_Handle* result) {
+ ASSERT(isolate != NULL);
+ const Error& error = Error::Handle(isolate, Library::CompileAll());
+ if (error.IsNull()) {
+ *result = Api::Success();
+ } else {
+ *result = Api::NewHandle(isolate, error.raw());
+ }
+}
+
+
+DART_EXPORT Dart_Handle Dart_CompileAll() {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ Dart_Handle result = Api::CheckIsolateState(isolate);
+ if (::Dart_IsError(result)) {
+ return result;
+ }
+ CHECK_CALLBACK_STATE(isolate);
+ CompileAll(isolate, &result);
+ return result;
+}
+
+
+DART_EXPORT Dart_Handle Dart_CheckFunctionFingerprints() {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ Dart_Handle result = Api::CheckIsolateState(isolate);
+ if (::Dart_IsError(result)) {
+ return result;
+ }
+ CHECK_CALLBACK_STATE(isolate);
+ Library::CheckFunctionFingerprints();
+ return result;
+}
+
+} // namespace dart
diff --git a/runtime/vm/native_message_handler.h b/runtime/vm/native_message_handler.h
index d3f29a8..777cb7f 100644
--- a/runtime/vm/native_message_handler.h
+++ b/runtime/vm/native_message_handler.h
@@ -6,6 +6,7 @@
#define VM_NATIVE_MESSAGE_HANDLER_H_
#include "include/dart_api.h"
+#include "include/dart_native_api.h"
#include "vm/message_handler.h"
namespace dart {
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 12cfe2d..c0e5a65 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -22,6 +22,7 @@
#include "vm/datastream.h"
#include "vm/debugger.h"
#include "vm/deopt_instructions.h"
+#include "vm/disassembler.h"
#include "vm/double_conversion.h"
#include "vm/exceptions.h"
#include "vm/flow_graph_builder.h"
@@ -7292,6 +7293,7 @@
case PcDescriptors::kClosureCall: return "closure-call ";
case PcDescriptors::kReturn: return "return ";
case PcDescriptors::kRuntimeCall: return "runtime-call ";
+ case PcDescriptors::kOsrEntry: return "osr-entry ";
case PcDescriptors::kOther: return "other ";
}
UNREACHABLE();
@@ -7999,6 +8001,13 @@
}
+void Code::Disassemble() const {
+ const Instructions& instr = Instructions::Handle(instructions());
+ uword start = instr.EntryPoint();
+ Disassembler::Disassemble(start, start + instr.size(), comments());
+}
+
+
const Code::Comments& Code::comments() const {
Comments* comments = new Code::Comments(Array::Handle(raw_ptr()->comments_));
return *comments;
@@ -8151,6 +8160,18 @@
}
+intptr_t Code::GetDeoptIdForOsr(uword pc) const {
+ const PcDescriptors& descriptors = PcDescriptors::Handle(pc_descriptors());
+ for (intptr_t i = 0; i < descriptors.Length(); ++i) {
+ if ((descriptors.PC(i) == pc) &&
+ (descriptors.DescriptorKind(i) == PcDescriptors::kOsrEntry)) {
+ return descriptors.DeoptId(i);
+ }
+ }
+ return Isolate::kNoDeoptId;
+}
+
+
const char* Code::ToCString() const {
const char* kFormat = "Code entry:%p";
intptr_t len = OS::SNPrint(NULL, 0, kFormat, EntryPoint()) + 1;
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 90e55f8..1f847b3 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2542,6 +2542,7 @@
kClosureCall, // Closure call.
kRuntimeCall, // Runtime call.
kReturn, // Return from function.
+ kOsrEntry, // OSR entry point in unoptimized code.
kOther
};
@@ -2877,6 +2878,8 @@
// Aborts if there is no static call at 'pc'.
void SetStaticCallTargetCodeAt(uword pc, const Code& code) const;
+ void Disassemble() const;
+
class Comments : public ZoneAllocated {
public:
static Comments& New(intptr_t count);
@@ -2964,6 +2967,7 @@
uword GetLazyDeoptPc() const;
uword GetPcForDeoptId(intptr_t deopt_id, PcDescriptors::Kind kind) const;
+ intptr_t GetDeoptIdForOsr(uword pc) const;
// Returns true if there is an object in the code between 'start_offset'
// (inclusive) and 'end_offset' (exclusive).
diff --git a/runtime/vm/runtime_entry_mips.cc b/runtime/vm/runtime_entry_mips.cc
index 622f24f..f9d2b2f 100644
--- a/runtime/vm/runtime_entry_mips.cc
+++ b/runtime/vm/runtime_entry_mips.cc
@@ -28,11 +28,17 @@
uword entry = GetEntryPoint();
#if defined(USING_SIMULATOR)
// Redirection to leaf runtime calls supports a maximum of 4 arguments passed
- // in registers.
- ASSERT(!is_leaf() || (argument_count() <= 4));
+ // in registers (maximum 2 double arguments for leaf float runtime calls).
+ ASSERT(argument_count() >= 0);
+ ASSERT(!is_leaf() ||
+ (!is_float() && (argument_count() <= 4)) ||
+ (argument_count() <= 2));
Simulator::CallKind call_kind =
- is_leaf() ? Simulator::kLeafRuntimeCall : Simulator::kRuntimeCall;
- entry = Simulator::RedirectExternalReference(entry, call_kind);
+ is_leaf() ? (is_float() ? Simulator::kLeafFloatRuntimeCall
+ : Simulator::kLeafRuntimeCall)
+ : Simulator::kRuntimeCall;
+ entry =
+ Simulator::RedirectExternalReference(entry, call_kind, argument_count());
#endif
if (is_leaf()) {
ExternalLabel label(name(), entry);
diff --git a/runtime/vm/simulator_mips.cc b/runtime/vm/simulator_mips.cc
index 3687bc9..0a12de3 100644
--- a/runtime/vm/simulator_mips.cc
+++ b/runtime/vm/simulator_mips.cc
@@ -92,6 +92,7 @@
bool GetValue(char* desc, uint32_t* value);
bool GetFValue(char* desc, double* value);
+ bool GetDValue(char* desc, double* value);
// Set or delete a breakpoint. Returns true if successful.
bool SetBreakpoint(Instr* breakpc);
@@ -226,6 +227,26 @@
}
+bool SimulatorDebugger::GetDValue(char* desc, double* value) {
+ FRegister freg = LookupFRegisterByName(desc);
+ if (freg != kNoFRegister) {
+ *value = sim_->get_fregister_double(freg);
+ return true;
+ }
+ if (desc[0] == '*') {
+ uint32_t addr;
+ if (GetValue(desc + 1, &addr)) {
+ if (Simulator::IsIllegalAddress(addr)) {
+ return false;
+ }
+ *value = *(reinterpret_cast<double*>(addr));
+ return true;
+ }
+ }
+ return false;
+}
+
+
bool SimulatorDebugger::SetBreakpoint(Instr* breakpc) {
// Check if a breakpoint can be set. If not return without any side-effects.
if (sim_->break_pc_ != NULL) {
@@ -361,6 +382,20 @@
} else {
OS::Print("printfloat <dreg or *addr>\n");
}
+ } else if ((strcmp(cmd, "pd") == 0) ||
+ (strcmp(cmd, "printdouble") == 0)) {
+ if (args == 2) {
+ double dvalue;
+ if (GetDValue(arg1, &dvalue)) {
+ uint64_t long_value = bit_cast<uint64_t, double>(dvalue);
+ OS::Print("%s: %llu 0x%llx %.8g\n",
+ arg1, long_value, long_value, dvalue);
+ } else {
+ OS::Print("%s unrecognized\n", arg1);
+ }
+ } else {
+ OS::Print("printfloat <dreg or *addr>\n");
+ }
} else if ((strcmp(cmd, "po") == 0) ||
(strcmp(cmd, "printobject") == 0)) {
if (args == 2) {
@@ -589,13 +624,16 @@
Simulator::CallKind call_kind() const { return call_kind_; }
+ int argument_count() const { return argument_count_; }
+
static Redirection* Get(uword external_function,
- Simulator::CallKind call_kind) {
+ Simulator::CallKind call_kind,
+ int argument_count) {
Redirection* current;
for (current = list_; current != NULL; current = current->next_) {
if (current->external_function_ == external_function) return current;
}
- return new Redirection(external_function, call_kind);
+ return new Redirection(external_function, call_kind, argument_count);
}
static Redirection* FromBreakInstruction(Instr* break_instruction) {
@@ -609,9 +647,12 @@
static const int32_t kRedirectInstruction =
Instr::kBreakPointInstruction | (Instr::kRedirectCode << kBreakCodeShift);
- Redirection(uword external_function, Simulator::CallKind call_kind)
+ Redirection(uword external_function,
+ Simulator::CallKind call_kind,
+ int argument_count)
: external_function_(external_function),
call_kind_(call_kind),
+ argument_count_(argument_count),
break_instruction_(kRedirectInstruction),
next_(list_) {
list_ = this;
@@ -619,6 +660,7 @@
uword external_function_;
Simulator::CallKind call_kind_;
+ int argument_count_;
uint32_t break_instruction_;
Redirection* next_;
static Redirection* list_;
@@ -628,8 +670,11 @@
Redirection* Redirection::list_ = NULL;
-uword Simulator::RedirectExternalReference(uword function, CallKind call_kind) {
- Redirection* redirection = Redirection::Get(function, call_kind);
+uword Simulator::RedirectExternalReference(uword function,
+ CallKind call_kind,
+ int argument_count) {
+ Redirection* redirection =
+ Redirection::Get(function, call_kind, argument_count);
return redirection->address_of_break_instruction();
}
@@ -890,6 +935,9 @@
typedef int32_t (*SimulatorLeafRuntimeCall)(
int32_t r0, int32_t r1, int32_t r2, int32_t r3);
+// Calls to leaf float Dart runtime functions are based on this interface.
+typedef double (*SimulatorLeafFloatRuntimeCall)(double d0, double d1);
+
// Calls to native Dart functions are based on this interface.
typedef void (*SimulatorNativeCall)(NativeArguments* arguments);
@@ -925,7 +973,8 @@
OS::Print("Call to host function at 0x%"Pd"\n", external);
}
- if (redirection->call_kind() != kLeafRuntimeCall) {
+ if ((redirection->call_kind() == kRuntimeCall) ||
+ (redirection->call_kind() == kNativeCall)) {
// The top_exit_frame_info of the current isolate points to the top of
// the simulator stack.
ASSERT((StackTop() - Isolate::Current()->top_exit_frame_info()) <
@@ -953,6 +1002,17 @@
reinterpret_cast<SimulatorLeafRuntimeCall>(external);
a0 = target(a0, a1, a2, a3);
set_register(V0, a0); // Set returned result from function.
+ } else if (redirection->call_kind() == kLeafFloatRuntimeCall) {
+ ASSERT((0 <= redirection->argument_count()) &&
+ (redirection->argument_count() <= 2));
+ // double values are passed and returned in floating point registers.
+ SimulatorLeafFloatRuntimeCall target =
+ reinterpret_cast<SimulatorLeafFloatRuntimeCall>(external);
+ double d0 = 0.0;
+ double d6 = get_fregister_double(F12);
+ double d7 = get_fregister_double(F14);
+ d0 = target(d6, d7);
+ set_fregister_double(F0, d0);
} else {
ASSERT(redirection->call_kind() == kNativeCall);
NativeArguments* arguments;
@@ -986,7 +1046,7 @@
// Zap floating point registers.
int32_t zap_dvalue = icount_;
- for (int i = F0; i <= F31; i++) {
+ for (int i = F4; i <= F18; i++) {
set_fregister(static_cast<FRegister>(i), zap_dvalue);
}
@@ -1109,6 +1169,20 @@
set_register(instr->RdField(), get_lo_register());
break;
}
+ case MOVCI: {
+ ASSERT(instr->SaField() == 0);
+ ASSERT(instr->Bit(17) == 0);
+ int32_t rs_val = get_register(instr->RsField());
+ uint32_t cc, fcsr_cc, test, status;
+ cc = instr->Bits(18, 3);
+ fcsr_cc = get_fcsr_condition_bit(cc);
+ test = instr->Bit(16);
+ status = test_fcsr_bit(fcsr_cc);
+ if (test == status) {
+ set_register(instr->RdField(), rs_val);
+ }
+ break;
+ }
case MOVN: {
ASSERT(instr->SaField() == 0);
// Format(instr, "movn 'rd, 'rs, 'rt");
@@ -1520,6 +1594,12 @@
set_fregister_double(instr->FdField(), fs_dbl);
break;
}
+ case FMT_S: {
+ float fs_flt = get_fregister_float(instr->FsField());
+ double fs_dbl = static_cast<double>(fs_flt);
+ set_fregister_double(instr->FdField(), fs_dbl);
+ break;
+ }
case FMT_L: {
int64_t fs_int = get_fregister_long(instr->FsField());
double fs_dbl = static_cast<double>(fs_int);
diff --git a/runtime/vm/simulator_mips.h b/runtime/vm/simulator_mips.h
index eca271d..9b685b2 100644
--- a/runtime/vm/simulator_mips.h
+++ b/runtime/vm/simulator_mips.h
@@ -106,9 +106,12 @@
enum CallKind {
kRuntimeCall,
kLeafRuntimeCall,
+ kLeafFloatRuntimeCall,
kNativeCall
};
- static uword RedirectExternalReference(uword function, CallKind call_kind);
+ static uword RedirectExternalReference(uword function,
+ CallKind call_kind,
+ int argument_count);
void Longjmp(uword pc,
uword sp,
diff --git a/runtime/vm/stack_frame.h b/runtime/vm/stack_frame.h
index 2b206fa..e32f242 100644
--- a/runtime/vm/stack_frame.h
+++ b/runtime/vm/stack_frame.h
@@ -75,7 +75,6 @@
// Returns token_pos of the pc(), or -1 if none exists.
intptr_t GetTokenPos() const;
-
protected:
StackFrame() : fp_(0), sp_(0), pc_(0) { }
@@ -296,4 +295,3 @@
} // namespace dart
#endif // VM_STACK_FRAME_H_
-
diff --git a/runtime/vm/stub_code_ia32.cc b/runtime/vm/stub_code_ia32.cc
index 0cf7c9b..7f884622f 100644
--- a/runtime/vm/stub_code_ia32.cc
+++ b/runtime/vm/stub_code_ia32.cc
@@ -1435,22 +1435,10 @@
__ popl(func_reg); // Restore.
__ LeaveFrame();
}
- Label is_hot;
- if (FlowGraphCompiler::CanOptimize()) {
- ASSERT(FLAG_optimization_counter_threshold > 1);
- __ cmpl(FieldAddress(func_reg, Function::usage_counter_offset()),
- Immediate(FLAG_optimization_counter_threshold));
- __ j(GREATER_EQUAL, &is_hot, Assembler::kNearJump);
- // As long as VM has no OSR do not optimize in the middle of the function
- // but only at exit so that we have collected all type feedback before
- // optimizing.
- }
__ incl(FieldAddress(func_reg, Function::usage_counter_offset()));
- __ Bind(&is_hot);
}
-
// Loads function into 'temp_reg'.
void StubCode::GenerateUsageCounterIncrement(Assembler* assembler,
Register temp_reg) {
@@ -1458,20 +1446,7 @@
Register func_reg = temp_reg;
ASSERT(ic_reg != func_reg);
__ movl(func_reg, FieldAddress(ic_reg, ICData::function_offset()));
- Label is_hot;
- if (FlowGraphCompiler::CanOptimize()) {
- ASSERT(FLAG_optimization_counter_threshold > 1);
- // The usage_counter is always less than FLAG_optimization_counter_threshold
- // except when the function gets optimized.
- __ cmpl(FieldAddress(func_reg, Function::usage_counter_offset()),
- Immediate(FLAG_optimization_counter_threshold));
- __ j(EQUAL, &is_hot, Assembler::kNearJump);
- // As long as VM has no OSR do not optimize in the middle of the function
- // but only at exit so that we have collected all type feedback before
- // optimizing.
- }
__ incl(FieldAddress(func_reg, Function::usage_counter_offset()));
- __ Bind(&is_hot);
}
diff --git a/runtime/vm/stub_code_mips.cc b/runtime/vm/stub_code_mips.cc
index e5cf95c..4bfd8a1 100644
--- a/runtime/vm/stub_code_mips.cc
+++ b/runtime/vm/stub_code_mips.cc
@@ -95,11 +95,11 @@
// Load Context pointer from Isolate structure into A2.
__ lw(A2, Address(CTX, Isolate::top_context_offset()));
- // Reload NULLREG.
- __ LoadImmediate(NULLREG, reinterpret_cast<intptr_t>(Object::null()));
+ // Load null.
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
// Reset Context pointer in Isolate structure.
- __ sw(NULLREG, Address(CTX, Isolate::top_context_offset()));
+ __ sw(TMP, Address(CTX, Isolate::top_context_offset()));
// Cache Context pointer into CTX while executing Dart code.
__ mov(CTX, A2);
@@ -202,11 +202,11 @@
// Load Context pointer from Isolate structure into A2.
__ lw(A2, Address(CTX, Isolate::top_context_offset()));
- // Reload NULLREG.
- __ LoadImmediate(NULLREG, reinterpret_cast<intptr_t>(Object::null()));
+ // Load null.
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
// Reset Context pointer in Isolate structure.
- __ sw(NULLREG, Address(CTX, Isolate::top_context_offset()));
+ __ sw(TMP, Address(CTX, Isolate::top_context_offset()));
// Cache Context pointer into CTX while executing Dart code.
__ mov(CTX, A2);
@@ -228,7 +228,8 @@
__ addiu(SP, SP, Immediate(-2 * kWordSize));
__ sw(S4, Address(SP, 1 * kWordSize));
- __ sw(NULLREG, Address(SP, 0 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 0 * kWordSize));
__ CallRuntime(kPatchStaticCallRuntimeEntry);
__ TraceSimMsg("CallStaticFunctionStub return");
@@ -257,7 +258,8 @@
// Setup space on stack for return value and preserve arguments descriptor.
__ addiu(SP, SP, Immediate(-2 * kWordSize));
__ sw(S4, Address(SP, 1 * kWordSize));
- __ sw(NULLREG, Address(SP, 0 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 0 * kWordSize));
__ CallRuntime(kFixCallersTargetRuntimeEntry);
// Get Code object result and restore arguments descriptor array.
__ lw(T0, Address(SP, 0 * kWordSize));
@@ -279,7 +281,7 @@
static void PushArgumentsArray(Assembler* assembler) {
__ TraceSimMsg("PushArgumentsArray");
// Allocate array to store arguments of caller.
- __ mov(A0, NULLREG);
+ __ LoadImmediate(A0, reinterpret_cast<intptr_t>(Object::null()));
// A0: Null element type for raw Array.
// A1: Smi-tagged argument count, may be zero.
__ BranchLink(&StubCode::AllocateArrayLabel());
@@ -329,7 +331,8 @@
// Push TMP1 data object.
// Push arguments descriptor array.
__ addiu(SP, SP, Immediate(-4 * kWordSize));
- __ sw(NULLREG, Address(SP, 3 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 3 * kWordSize));
__ sw(T1, Address(SP, 2 * kWordSize));
__ sw(S5, Address(SP, 1 * kWordSize));
__ sw(S4, Address(SP, 0 * kWordSize));
@@ -498,8 +501,44 @@
void StubCode::GenerateMegamorphicMissStub(Assembler* assembler) {
- __ Unimplemented("MegamorphicMiss stub");
- return;
+ __ EnterStubFrame();
+
+ // Load the receiver.
+ __ lw(T2, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
+ __ sll(T2, T2, 1); // T2 is a Smi.
+ __ addu(TMP, FP, T2);
+ __ lw(T6, Address(TMP, kParamEndSlotFromFp * kWordSize));
+
+ // Preserve IC data and arguments descriptor.
+ __ addiu(SP, SP, Immediate(-6 * kWordSize));
+ __ sw(S5, Address(SP, 5 * kWordSize));
+ __ sw(S4, Address(SP, 4 * kWordSize));
+
+ // Push space for the return value.
+ // Push the receiver.
+ // Push IC data object.
+ // Push arguments descriptor array.
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 3 * kWordSize));
+ __ sw(T6, Address(SP, 2 * kWordSize));
+ __ sw(S5, Address(SP, 1 * kWordSize));
+ __ sw(S4, Address(SP, 0 * kWordSize));
+
+ __ CallRuntime(kMegamorphicCacheMissHandlerRuntimeEntry);
+
+ __ lw(T0, Address(SP, 3 * kWordSize)); // Get result.
+ __ lw(S4, Address(SP, 4 * kWordSize)); // Restore argument descriptor.
+ __ lw(S5, Address(SP, 5 * kWordSize)); // Restore IC data.
+ __ addiu(SP, SP, Immediate(6 * kWordSize));
+
+ __ LeaveStubFrame();
+
+ Label nonnull;
+ __ BranchNotEqual(T0, reinterpret_cast<int32_t>(Object::null()), &nonnull);
+ __ Branch(&StubCode::InstanceFunctionLookupLabel());
+ __ Bind(&nonnull);
+ __ AddImmediate(T0, Instructions::HeaderSize() - kHeapObjectTag);
+ __ jr(T0);
}
@@ -604,12 +643,13 @@
// T3: iterator which initially points to the start of the variable
// data area to be initialized.
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
Label loop, loop_exit;
__ BranchUnsignedGreaterEqual(T3, T2, &loop_exit);
__ Bind(&loop);
__ addiu(T3, T3, Immediate(kWordSize));
__ bne(T3, T2, &loop);
- __ delay_slot()->sw(NULLREG, Address(T3, -kWordSize));
+ __ delay_slot()->sw(T7, Address(T3, -kWordSize));
__ Bind(&loop_exit);
// Done allocating and initializing the array.
@@ -627,17 +667,17 @@
// Setup space on stack for return value.
// Push array length as Smi and element type.
__ addiu(SP, SP, Immediate(-3 * kWordSize));
- __ sw(NULLREG, Address(SP, 2 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 2 * kWordSize));
__ sw(A1, Address(SP, 1 * kWordSize));
- __ sw(T3, Address(SP, 0 * kWordSize));
+ __ sw(A0, Address(SP, 0 * kWordSize));
__ CallRuntime(kAllocateArrayRuntimeEntry);
__ TraceSimMsg("AllocateArrayStub return");
// Pop arguments; result is popped in IP.
- __ lw(TMP1, Address(SP, 2 * kWordSize));
+ __ lw(V0, Address(SP, 2 * kWordSize));
__ lw(A1, Address(SP, 1 * kWordSize));
- __ lw(T3, Address(SP, 0 * kWordSize));
+ __ lw(A0, Address(SP, 0 * kWordSize));
__ addiu(SP, SP, Immediate(3 * kWordSize));
- __ mov(V0, TMP1);
__ LeaveStubFrameAndReturn();
}
@@ -666,8 +706,10 @@
// Verify that T1 is a closure by checking its class.
Label not_closure;
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
+
// See if it is not a closure, but null object.
- __ beq(T1, NULLREG, ¬_closure);
+ __ beq(T1, T7, ¬_closure);
__ andi(CMPRES, T1, Immediate(kSmiTagMask));
__ beq(CMPRES, ZR, ¬_closure); // Not a closure, but a smi.
@@ -678,7 +720,7 @@
__ lw(T0, FieldAddress(T0, Class::signature_function_offset()));
// See if actual class is not a closure class.
- __ beq(T0, NULLREG, ¬_closure);
+ __ beq(T0, T7, ¬_closure);
// T0 is just the signature function. Load the actual closure function.
__ lw(T2, FieldAddress(T1, Closure::function_offset()));
@@ -689,7 +731,7 @@
Label function_compiled;
// Load closure function code in T0.
__ lw(T0, FieldAddress(T2, Function::code_offset()));
- __ bne(T0, NULLREG, &function_compiled);
+ __ bne(T0, T7, &function_compiled);
// Create a stub frame as we are pushing some objects on the stack before
// calling into the runtime.
@@ -731,9 +773,9 @@
__ EnterStubFrame();
// Setup space on stack for result from error reporting.
- __ addiu(SP, SP, Immediate(2 * kWordSize));
+ __ addiu(SP, SP, Immediate(-2 * kWordSize));
// Arguments descriptor and raw null.
- __ sw(NULLREG, Address(SP, 1 * kWordSize));
+ __ sw(T7, Address(SP, 1 * kWordSize));
__ sw(S4, Address(SP, 0 * kWordSize));
// Load smi-tagged arguments array length, including the non-closure.
@@ -825,9 +867,6 @@
// Compute address of 'arguments array' data area into A2.
__ lw(A2, Address(A2, VMHandles::kOffsetOfRawPtrInHandle));
- // Load the null Object into NULLREG for easy comparisons.
- __ LoadImmediate(NULLREG, reinterpret_cast<intptr_t>(Object::null()));
-
// Set up arguments for the Dart call.
Label push_arguments;
Label done_push_arguments;
@@ -960,20 +999,22 @@
// T2: isolate, not an object.
__ sw(T2, FieldAddress(V0, Context::isolate_offset()));
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
+
// Initialize the context variables.
// V0: new object.
// T1: number of context variables.
Label loop, loop_exit;
__ blez(T1, &loop_exit);
// Setup the parent field.
- __ delay_slot()->sw(NULLREG, FieldAddress(V0, Context::parent_offset()));
+ __ delay_slot()->sw(T7, FieldAddress(V0, Context::parent_offset()));
__ AddImmediate(T3, V0, Context::variable_offset(0) - kHeapObjectTag);
__ sll(T1, T1, 2);
__ Bind(&loop);
__ addiu(T1, T1, Immediate(-kWordSize));
__ addu(TMP1, T3, T1);
__ bgtz(T1, &loop);
- __ delay_slot()->sw(NULLREG, Address(TMP1));
+ __ delay_slot()->sw(T7, Address(TMP1));
__ Bind(&loop_exit);
// Done allocating and initializing the context.
@@ -988,7 +1029,8 @@
// Setup space on stack for return value.
__ SmiTag(T1);
__ addiu(SP, SP, Immediate(-2 * kWordSize));
- __ sw(NULLREG, Address(SP, 1 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 1 * kWordSize)); // Store null.
__ sw(T1, Address(SP, 0 * kWordSize));
__ CallRuntime(kAllocateContextRuntimeEntry); // Allocate context.
__ lw(V0, Address(SP, 1 * kWordSize)); // Get the new context.
@@ -1172,6 +1214,8 @@
__ LoadImmediate(T0, tags);
__ sw(T0, Address(T2, Instance::tags_offset()));
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
+
// Initialize the remaining words of the object.
// T2: new object start.
// T3: next object start.
@@ -1183,7 +1227,7 @@
for (intptr_t current_offset = sizeof(RawObject);
current_offset < instance_size;
current_offset += kWordSize) {
- __ sw(NULLREG, Address(T2, current_offset));
+ __ sw(T7, Address(T2, current_offset));
}
} else {
__ addiu(T4, T2, Immediate(sizeof(RawObject)));
@@ -1197,7 +1241,7 @@
__ Bind(&loop);
__ addiu(T4, T4, Immediate(kWordSize));
__ bne(T4, T3, &loop);
- __ delay_slot()->sw(NULLREG, Address(T4, -kWordSize));
+ __ delay_slot()->sw(T7, Address(T4, -kWordSize));
__ Bind(&loop_exit);
}
if (is_cls_parameterized) {
@@ -1223,7 +1267,8 @@
__ addiu(SP, SP, Immediate(-4 * kWordSize));
// Space on stack for return value.
- __ sw(NULLREG, Address(SP, 3 * kWordSize));
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(T7, Address(SP, 3 * kWordSize));
__ sw(TMP1, Address(SP, 2 * kWordSize)); // Class of object to be allocated.
if (is_cls_parameterized) {
@@ -1233,7 +1278,7 @@
} else {
// Push null type arguments and kNoInstantiator.
__ LoadImmediate(T1, Smi::RawValue(StubCode::kNoInstantiator));
- __ sw(NULLREG, Address(SP, 1 * kWordSize));
+ __ sw(T7, Address(SP, 1 * kWordSize));
__ sw(T1, Address(SP, 0 * kWordSize));
}
__ CallRuntime(kAllocateObjectRuntimeEntry); // Allocate object.
@@ -1264,8 +1309,8 @@
__ TraceSimMsg("AllocationStubForClosure");
__ EnterStubFrame(true); // Uses pool pointer to refer to function.
- const intptr_t kTypeArgumentsFPOffset = 4 * kWordSize;
- const intptr_t kReceiverFPOffset = 5 * kWordSize;
+ const intptr_t kTypeArgumentsFPOffset = 3 * kWordSize;
+ const intptr_t kReceiverFPOffset = 4 * kWordSize;
const intptr_t closure_size = Closure::InstanceSize();
const intptr_t context_size = Context::InstanceSize(1); // Captured receiver.
if (FLAG_inline_alloc &&
@@ -1337,7 +1382,8 @@
__ sw(T0, Address(T4, Context::isolate_offset()));
// Set the parent to null.
- __ sw(NULLREG, Address(T4, Context::parent_offset()));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(T4, Context::parent_offset()));
// Initialize the context variable to the receiver.
__ lw(T0, Address(FP, kReceiverFPOffset));
@@ -1370,24 +1416,24 @@
num_slots = is_implicit_instance_closure ? 4 : 3;
}
__ addiu(SP, SP, Immediate(-num_slots * kWordSize));
- __ LoadObject(TMP1, func);
// Setup space on stack for return value.
- __ sw(NULLREG, Address(SP, (num_slots - 1) * kWordSize));
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(T7, Address(SP, (num_slots - 1) * kWordSize));
+ __ LoadObject(TMP1, func);
__ sw(TMP1, Address(SP, (num_slots - 2) * kWordSize));
if (is_implicit_static_closure) {
__ CallRuntime(kAllocateImplicitStaticClosureRuntimeEntry);
__ TraceSimMsg("AllocationStubForClosure return");
} else {
+ __ mov(T2, T7);
if (is_implicit_instance_closure) {
__ lw(T1, Address(FP, kReceiverFPOffset));
__ sw(T1, Address(SP, (num_slots - 3) * kWordSize)); // Receiver.
- __ sw(NULLREG, Address(SP, (num_slots - 4) * kWordSize)); // Push null.
}
if (has_type_arguments) {
- __ lw(V0, Address(FP, kTypeArgumentsFPOffset));
- // Push type arguments of closure.
- __ sw(V0, Address(SP, (num_slots - 3) * kWordSize));
+ __ lw(T2, Address(FP, kTypeArgumentsFPOffset));
}
+ __ sw(T2, Address(SP, 0 * kWordSize));
if (is_implicit_instance_closure) {
__ CallRuntime(kAllocateImplicitInstanceClosureRuntimeEntry);
@@ -1407,8 +1453,37 @@
}
+// The target function was not found, so invoke method
+// "dynamic noSuchMethod(Invocation invocation)".
+// S5: inline cache data object.
+// S4: arguments descriptor array.
void StubCode::GenerateCallNoSuchMethodFunctionStub(Assembler* assembler) {
- __ Unimplemented("CallNoSuchMethodFunction stub");
+ __ EnterStubFrame();
+
+ // Load the receiver.
+ __ lw(A1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
+ __ sll(TMP, A1, 1); // A1 is a Smi.
+ __ addu(TMP, FP, TMP);
+ __ lw(T6, Address(TMP, kParamEndSlotFromFp * kWordSize));
+
+ // Push space for the return value.
+ // Push the receiver.
+ // Push IC data object.
+ // Push arguments descriptor array.
+ __ addiu(SP, SP, Immediate(-4 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 3 * kWordSize));
+ __ sw(T6, Address(SP, 2 * kWordSize));
+ __ sw(S5, Address(SP, 1 * kWordSize));
+ __ sw(S4, Address(SP, 0 * kWordSize));
+
+ // A1: Smi-tagged arguments array length.
+ PushArgumentsArray(assembler);
+
+ __ CallRuntime(kInvokeNoSuchMethodFunctionRuntimeEntry);
+
+ __ lw(V0, Address(SP, 4 * kWordSize)); // Get result into V0.
+ __ LeaveStubFrameAndReturn();
}
@@ -1585,7 +1660,8 @@
__ addiu(SP, SP, Immediate(-num_slots * kWordSize));
__ sw(S5, Address(SP, (num_slots - 1) * kWordSize));
__ sw(S4, Address(SP, (num_slots - 2) * kWordSize));
- __ sw(NULLREG, Address(SP, (num_slots - 3) * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, (num_slots - 3) * kWordSize));
// Push call arguments.
for (intptr_t i = 0; i < num_args; i++) {
__ lw(TMP1, Address(T1, -i * kWordSize));
@@ -1615,7 +1691,8 @@
__ addiu(SP, SP, Immediate(num_slots * kWordSize));
__ LeaveStubFrame();
Label call_target_function;
- __ bne(T3, NULLREG, &call_target_function);
+ __ BranchNotEqual(T3, reinterpret_cast<int32_t>(Object::null()),
+ &call_target_function);
// NoSuchMethod or closure.
// Mark IC call that it may be a closure call that does not collect
@@ -1737,7 +1814,8 @@
// Preserve arguments descriptor and make room for result.
__ addiu(SP, SP, Immediate(-2 * kWordSize));
__ sw(S4, Address(SP, 1 * kWordSize));
- __ sw(NULLREG, Address(SP, 0 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 0 * kWordSize));
__ CallRuntime(kBreakpointStaticHandlerRuntimeEntry);
// Pop code object result and restore arguments descriptor.
__ lw(T0, Address(SP, 0 * kWordSize));
@@ -1837,14 +1915,17 @@
__ lw(T2, FieldAddress(A2, SubtypeTestCache::cache_offset()));
__ AddImmediate(T2, Array::data_offset() - kHeapObjectTag);
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
+
Label loop, found, not_found, next_iteration;
// T0: instance class id.
// T1: instance type arguments.
// T2: Entry start.
+ // T7: null.
__ SmiTag(T0);
__ Bind(&loop);
__ lw(T3, Address(T2, kWordSize * SubtypeTestCache::kInstanceClassId));
- __ beq(T3, NULLREG, ¬_found);
+ __ beq(T3, T7, ¬_found);
if (n == 1) {
__ beq(T3, T0, &found);
@@ -1868,7 +1949,7 @@
// Fall through to not found.
__ Bind(¬_found);
__ Ret();
- __ delay_slot()->mov(V0, NULLREG);
+ __ delay_slot()->mov(V0, T7);
__ Bind(&found);
__ Ret();
@@ -1947,6 +2028,7 @@
// TODO(srdjan): Move to VM stubs once Boolean objects become VM objects.
void StubCode::GenerateEqualityWithNullArgStub(Assembler* assembler) {
__ TraceSimMsg("EqualityWithNullArgStub");
+ __ Comment("EqualityWithNullArgStub");
__ EnterStubFrame();
static const intptr_t kNumArgsTested = 2;
#if defined(DEBUG)
@@ -2048,7 +2130,8 @@
__ addiu(SP, SP, Immediate(-3 * kWordSize));
__ sw(S4, Address(SP, 2 * kWordSize));
// Setup space on stack for return value.
- __ sw(NULLREG, Address(SP, 1 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 1 * kWordSize));
__ sw(T0, Address(SP, 0 * kWordSize));
__ CallRuntime(kOptimizeInvokedFunctionRuntimeEntry);
__ TraceSimMsg("OptimizeFunctionStub return");
@@ -2079,21 +2162,23 @@
// cannot contain a value that fits in Mint or Smi.
void StubCode::GenerateIdenticalWithNumberCheckStub(Assembler* assembler) {
__ TraceSimMsg("IdenticalWithNumberCheckStub");
- const Register ret = CMPRES;
+ __ Comment("IdenticalWithNumberCheckStub");
const Register temp1 = T2;
const Register temp2 = T3;
const Register left = T1;
const Register right = T0;
// Preserve left, right.
- __ addiu(SP, SP, Immediate(-2 * kWordSize));
- __ sw(T1, Address(SP, 1 * kWordSize));
- __ sw(T0, Address(SP, 0 * kWordSize));
+ __ addiu(SP, SP, Immediate(-4 * kWordSize));
+ __ sw(temp1, Address(SP, 3 * kWordSize));
+ __ sw(temp2, Address(SP, 2 * kWordSize));
+ __ sw(left, Address(SP, 1 * kWordSize));
+ __ sw(right, Address(SP, 0 * kWordSize));
// TOS + 3: left argument.
// TOS + 2: right argument.
// TOS + 1: saved left
// TOS + 0: saved right
- __ lw(left, Address(SP, 3 * kWordSize));
- __ lw(right, Address(SP, 2 * kWordSize));
+ __ lw(left, Address(SP, 5 * kWordSize));
+ __ lw(right, Address(SP, 4 * kWordSize));
Label reference_compare, done, check_mint, check_bigint;
// If any of the arguments is Smi do reference compare.
__ andi(temp1, left, Immediate(kSmiTagMask));
@@ -2106,48 +2191,50 @@
__ LoadClassId(temp2, left);
__ bne(temp1, temp2, &check_mint);
__ LoadClassId(temp2, right);
- __ subu(ret, temp1, temp2);
- __ bne(ret, ZR, &done);
+ __ subu(CMPRES, temp1, temp2);
+ __ bne(CMPRES, ZR, &done);
// Double values bitwise compare.
__ lw(temp1, FieldAddress(left, Double::value_offset() + 0 * kWordSize));
- __ lw(temp1, FieldAddress(right, Double::value_offset() + 0 * kWordSize));
- __ subu(ret, temp1, temp2);
- __ bne(ret, ZR, &done);
+ __ lw(temp2, FieldAddress(right, Double::value_offset() + 0 * kWordSize));
+ __ subu(CMPRES, temp1, temp2);
+ __ bne(CMPRES, ZR, &done);
__ lw(temp1, FieldAddress(left, Double::value_offset() + 1 * kWordSize));
__ lw(temp2, FieldAddress(right, Double::value_offset() + 1 * kWordSize));
__ b(&done);
- __ delay_slot()->subu(ret, temp1, temp2);
+ __ delay_slot()->subu(CMPRES, temp1, temp2);
__ Bind(&check_mint);
__ LoadImmediate(temp1, kMintCid);
__ LoadClassId(temp2, left);
__ bne(temp1, temp2, &check_bigint);
__ LoadClassId(temp2, right);
- __ subu(ret, temp1, temp2);
- __ bne(ret, ZR, &done);
+ __ subu(CMPRES, temp1, temp2);
+ __ bne(CMPRES, ZR, &done);
__ lw(temp1, FieldAddress(left, Mint::value_offset() + 0 * kWordSize));
__ lw(temp2, FieldAddress(right, Mint::value_offset() + 0 * kWordSize));
- __ subu(ret, temp1, temp2);
- __ bne(ret, ZR, &done);
+ __ subu(CMPRES, temp1, temp2);
+ __ bne(CMPRES, ZR, &done);
__ lw(temp1, FieldAddress(left, Mint::value_offset() + 1 * kWordSize));
__ lw(temp2, FieldAddress(right, Mint::value_offset() + 1 * kWordSize));
__ b(&done);
- __ delay_slot()->subu(ret, temp1, temp2);
+ __ delay_slot()->subu(CMPRES, temp1, temp2);
__ Bind(&check_bigint);
__ LoadImmediate(temp1, kBigintCid);
__ LoadClassId(temp2, left);
__ bne(temp1, temp2, &reference_compare);
__ LoadClassId(temp2, right);
- __ subu(ret, temp1, temp2);
- __ bne(ret, ZR, &done);
+ __ subu(CMPRES, temp1, temp2);
+ __ bne(CMPRES, ZR, &done);
- __ EnterStubFrame(0);
+ __ EnterStubFrame();
__ ReserveAlignedFrameSpace(2 * kWordSize);
- __ sw(T1, Address(SP, 1 * kWordSize));
- __ sw(T0, Address(SP, 0 * kWordSize));
+ __ sw(left, Address(SP, 1 * kWordSize));
+ __ sw(right, Address(SP, 0 * kWordSize));
+ __ mov(A0, left);
+ __ mov(A1, right);
__ CallRuntime(kBigintCompareRuntimeEntry);
__ TraceSimMsg("IdenticalWithNumberCheckStub return");
// Result in V0, 0 means equal.
@@ -2156,14 +2243,16 @@
__ delay_slot()->mov(CMPRES, V0);
__ Bind(&reference_compare);
- __ subu(ret, left, right);
+ __ subu(CMPRES, left, right);
__ Bind(&done);
// A branch or test after this comparison will check CMPRES == TMP1.
__ mov(TMP1, ZR);
- __ lw(T0, Address(SP, 0 * kWordSize));
- __ lw(T1, Address(SP, 1 * kWordSize));
+ __ lw(right, Address(SP, 0 * kWordSize));
+ __ lw(left, Address(SP, 1 * kWordSize));
+ __ lw(temp2, Address(SP, 2 * kWordSize));
+ __ lw(temp1, Address(SP, 3 * kWordSize));
__ Ret();
- __ delay_slot()->addiu(SP, SP, Immediate(2 * kWordSize));
+ __ delay_slot()->addiu(SP, SP, Immediate(4 * kWordSize));
}
} // namespace dart
diff --git a/runtime/vm/stub_code_x64.cc b/runtime/vm/stub_code_x64.cc
index 0b49f59..8d78a52 100644
--- a/runtime/vm/stub_code_x64.cc
+++ b/runtime/vm/stub_code_x64.cc
@@ -1418,18 +1418,7 @@
__ popq(func_reg); // Restore.
__ LeaveFrame();
}
- Label is_hot;
- if (FlowGraphCompiler::CanOptimize()) {
- ASSERT(FLAG_optimization_counter_threshold > 1);
- __ cmpq(FieldAddress(func_reg, Function::usage_counter_offset()),
- Immediate(FLAG_optimization_counter_threshold));
- __ j(GREATER_EQUAL, &is_hot, Assembler::kNearJump);
- // As long as VM has no OSR do not optimize in the middle of the function
- // but only at exit so that we have collected all type feedback before
- // optimizing.
- }
__ incq(FieldAddress(func_reg, Function::usage_counter_offset()));
- __ Bind(&is_hot);
}
@@ -1440,22 +1429,10 @@
Register func_reg = temp_reg;
ASSERT(ic_reg != func_reg);
__ movq(func_reg, FieldAddress(ic_reg, ICData::function_offset()));
- Label is_hot;
- if (FlowGraphCompiler::CanOptimize()) {
- ASSERT(FLAG_optimization_counter_threshold > 1);
- // The usage_counter is always less than FLAG_optimization_counter_threshold
- // except when the function gets optimized.
- __ cmpq(FieldAddress(func_reg, Function::usage_counter_offset()),
- Immediate(FLAG_optimization_counter_threshold));
- __ j(EQUAL, &is_hot, Assembler::kNearJump);
- // As long as VM has no OSR do not optimize in the middle of the function
- // but only at exit so that we have collected all type feedback before
- // optimizing.
- }
__ incq(FieldAddress(func_reg, Function::usage_counter_offset()));
- __ Bind(&is_hot);
}
+
// Generate inline cache check for 'num_args'.
// RBX: Inline cache data object.
// R10: Arguments descriptor array.
diff --git a/sdk/lib/_internal/compiler/implementation/compiler.dart b/sdk/lib/_internal/compiler/implementation/compiler.dart
index c850caa..75844c2 100644
--- a/sdk/lib/_internal/compiler/implementation/compiler.dart
+++ b/sdk/lib/_internal/compiler/implementation/compiler.dart
@@ -409,6 +409,7 @@
EnqueueTask enqueuer;
CompilerTask fileReadingTask;
DeferredLoadTask deferredLoadTask;
+ ContainerTracer containerTracer;
String buildId;
static const SourceString MAIN = const SourceString('main');
@@ -453,6 +454,9 @@
bool hasCrashed = false;
+ /// Set by the backend if real reflection is detected in use of dart:mirrors.
+ bool disableTypeInferenceForMirrors = false;
+
Compiler({this.tracer: const Tracer(),
this.enableTypeAssertions: false,
this.enableUserAssertions: false,
@@ -510,6 +514,7 @@
closureToClassMapper = new closureMapping.ClosureTask(this, closureNamer),
checker = new TypeCheckerTask(this),
typesTask = new ti.TypesTask(this),
+ containerTracer = new ContainerTracer(this),
constantHandler = new ConstantHandler(this, backend.constantSystem),
deferredLoadTask = new DeferredLoadTask(this),
enqueuer = new EnqueueTask(this)];
@@ -530,7 +535,9 @@
bool get compileAll => false;
- bool get disableTypeInference => disableTypeInferenceFlag || mirrorsEnabled;
+ bool get disableTypeInference {
+ return disableTypeInferenceFlag || disableTypeInferenceForMirrors;
+ }
int getNextFreeClassId() => nextFreeClassId++;
diff --git a/sdk/lib/_internal/compiler/implementation/dart2jslib.dart b/sdk/lib/_internal/compiler/implementation/dart2jslib.dart
index c006c95..517573f 100644
--- a/sdk/lib/_internal/compiler/implementation/dart2jslib.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart2jslib.dart
@@ -35,6 +35,7 @@
import 'resolution/resolution.dart';
import 'js/js.dart' as js;
import 'deferred_load.dart' show DeferredLoadTask;
+import 'types/container_tracer.dart' show ContainerTracer;
export 'resolution/resolution.dart' show TreeElements, TreeElementMapping;
export 'scanner/scannerlib.dart' show SourceString,
diff --git a/sdk/lib/_internal/compiler/implementation/enqueue.dart b/sdk/lib/_internal/compiler/implementation/enqueue.dart
index f3dae36..1352cea 100644
--- a/sdk/lib/_internal/compiler/implementation/enqueue.dart
+++ b/sdk/lib/_internal/compiler/implementation/enqueue.dart
@@ -639,8 +639,8 @@
if (uri == 'dart:isolate') {
enableIsolateSupport(library);
} else if (uri == 'dart:async') {
- ClassElement cls = element.getEnclosingClass();
- if (cls != null && cls.name == const SourceString('Timer')) {
+ if (element.name == const SourceString('_createTimer') ||
+ element.name == const SourceString('_createPeriodicTimer')) {
// The [:Timer:] class uses the event queue of the isolate
// library, so we make sure that event queue is generated.
enableIsolateSupport(library);
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
index 87e7294..5afa2a0 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
@@ -1133,6 +1133,7 @@
void registerStaticUse(Element element, Enqueuer enqueuer) {
if (element == disableTreeShakingMarker) {
enqueuer.enqueueEverything();
+ compiler.disableTypeInferenceForMirrors = true;
} else if (element == preserveNamesMarker) {
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
index 61e465b..75d6443 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
@@ -3341,11 +3341,15 @@
if (Elements.isFixedListConstructorCall(
originalElement, send, compiler)) {
isListConstructor = true;
- return backend.fixedArrayType;
+ HType inferred =
+ new HType.inferredForNode(currentElement, node, compiler);
+ return inferred.isUnknown() ? backend.fixedArrayType : inferred;
} else if (Elements.isGrowableListConstructorCall(
originalElement, send, compiler)) {
isListConstructor = true;
- return backend.extendableArrayType;
+ HType inferred =
+ new HType.inferredForNode(currentElement, node, compiler);
+ return inferred.isUnknown() ? backend.extendableArrayType : inferred;
} else if (element.isGenerativeConstructor()) {
ClassElement cls = element.getEnclosingClass();
return new HType.nonNullExact(cls.thisType, compiler);
@@ -4103,7 +4107,10 @@
visit(link.head);
inputs.add(pop());
}
- push(buildLiteralList(inputs));
+ HInstruction instruction = buildLiteralList(inputs);
+ HType type = new HType.inferredForNode(currentElement, node, compiler);
+ if (!type.isUnknown()) instruction.instructionType = type;
+ push(instruction);
}
visitConditional(Conditional node) {
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/invoke_dynamic_specializers.dart b/sdk/lib/_internal/compiler/implementation/ssa/invoke_dynamic_specializers.dart
index fdfd0db..9c5f1a9 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/invoke_dynamic_specializers.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/invoke_dynamic_specializers.dart
@@ -154,18 +154,18 @@
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
Compiler compiler) {
- if (instruction.inputs[1].isIndexable(compiler)) {
- if (!instruction.inputs[2].isInteger() && compiler.enableTypeAssertions) {
- // We want the right checked mode error.
- return null;
- }
- HInstruction index = new HIndex(
- instruction.inputs[1], instruction.inputs[2], instruction.selector);
- index.instructionType =
- new HType.inferredTypeForSelector(instruction.selector, compiler);
- return index;
+ if (!instruction.inputs[1].isIndexable(compiler)) return null;
+ if (!instruction.inputs[2].isInteger() && compiler.enableTypeAssertions) {
+ // We want the right checked mode error.
+ return null;
}
- return null;
+ HInstruction index = new HIndex(
+ instruction.inputs[1], instruction.inputs[2], instruction.selector);
+ HType receiverType = instruction.getDartReceiver(compiler).instructionType;
+ Selector refined = receiverType.refine(instruction.selector, compiler);
+ HType type = new HType.inferredTypeForSelector(refined, compiler);
+ index.instructionType = type;
+ return index;
}
bool hasBuiltinVariant(HInvokeDynamic instruction, Compiler compiler) {
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/tracer.dart b/sdk/lib/_internal/compiler/implementation/ssa/tracer.dart
index f408df4..44c01e8 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/tracer.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/tracer.dart
@@ -179,12 +179,18 @@
String temporaryId(HInstruction instruction) {
String prefix;
HType type = instruction.instructionType;
- if (type.isMutableArray(compiler)) {
+ if (type.isExtendableArray(compiler)) {
+ prefix = 'e';
+ } else if (type.isFixedArray(compiler)) {
+ prefix = 'f';
+ } else if (type.isMutableArray(compiler)) {
prefix = 'm';
} else if (type.isReadableArray(compiler)) {
prefix = 'a';
- } else if (type.isExtendableArray(compiler)) {
- prefix = 'e';
+ } else if (type.isString(compiler)) {
+ prefix = 's';
+ } else if (type.isIndexable(compiler)) {
+ prefix = 'r';
} else if (type == HType.BOOLEAN) {
prefix = 'b';
} else if (type == HType.INTEGER) {
@@ -193,14 +199,10 @@
prefix = 'd';
} else if (type == HType.NUMBER) {
prefix = 'n';
- } else if (type.isString(compiler)) {
- prefix = 's';
} else if (type == HType.UNKNOWN) {
prefix = 'v';
} else if (type == HType.CONFLICTING) {
prefix = 'c';
- } else if (type.isIndexable(compiler)) {
- prefix = 'r';
} else if (type == HType.NULL) {
prefix = 'u';
} else {
diff --git a/sdk/lib/_internal/compiler/implementation/types/container_tracer.dart b/sdk/lib/_internal/compiler/implementation/types/container_tracer.dart
new file mode 100644
index 0000000..04c9923
--- /dev/null
+++ b/sdk/lib/_internal/compiler/implementation/types/container_tracer.dart
@@ -0,0 +1,666 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 container_tracer;
+
+import '../dart2jslib.dart' hide Selector, TypedSelector;
+import '../elements/elements.dart';
+import '../tree/tree.dart';
+import '../universe/universe.dart';
+import '../util/util.dart' show Link;
+import 'simple_types_inferrer.dart' show SimpleTypesInferrer, InferrerVisitor;
+import 'types.dart';
+
+/**
+ * A set of selector names that [List] implements, that we know do not
+ * change the element type of the list, or let the list escape to code
+ * that might change the element type.
+ */
+Set<String> okSelectorsSet = new Set<String>.from(
+ const <String>[
+ // From Object.
+ '==',
+ 'hashCode',
+ 'toString',
+ 'noSuchMethod',
+ 'runtimeType',
+
+ // From Iterable.
+ 'iterator',
+ 'map',
+ 'where',
+ 'expand',
+ 'contains',
+ 'forEach',
+ 'reduce',
+ 'fold',
+ 'every',
+ 'join',
+ 'any',
+ 'toList',
+ 'toSet',
+ 'length',
+ 'isEmpty',
+ 'isNotEmpty',
+ 'take',
+ 'takeWhile',
+ 'skip',
+ 'skipWhile',
+ 'first',
+ 'last',
+ 'single',
+ 'firstWhere',
+ 'lastWhere',
+ 'singleWhere',
+ 'elementAt',
+
+ // From List.
+ '[]',
+ 'length',
+ 'reversed',
+ 'sort',
+ 'indexOf',
+ 'lastIndexOf',
+ 'clear',
+ 'remove',
+ 'removeAt',
+ 'removeLast',
+ 'removeWhere',
+ 'retainWhere',
+ 'sublist',
+ 'getRange',
+ 'removeRange',
+ 'asMap',
+
+ // From JSArray.
+ 'checkMutable',
+ 'checkGrowable',
+ ]);
+
+bool _VERBOSE = false;
+
+/**
+ * Global analysis phase that traces container instantiations in order to
+ * find their element type.
+ */
+class ContainerTracer extends CompilerTask {
+ ContainerTracer(Compiler compiler) : super(compiler);
+
+ String get name => 'List tracer';
+
+ bool analyze() {
+ measure(() {
+ SimpleTypesInferrer inferrer = compiler.typesTask.typesInferrer;
+ var internal = inferrer.internal;
+ // Walk over all created [ContainerTypeMask].
+ internal.concreteTypes.values.forEach((ContainerTypeMask mask) {
+ mask.elementType = new TracerForConcreteContainer(
+ mask, this, compiler, inferrer).run();
+ });
+ });
+ }
+}
+
+/**
+ * A tracer for a specific container.
+ */
+class TracerForConcreteContainer {
+ final Compiler compiler;
+ final ContainerTracer tracer;
+ final SimpleTypesInferrer inferrer;
+
+ final Node analyzedNode;
+ final Element startElement;
+
+ final List<Element> workList = <Element>[];
+
+ /**
+ * A set of elements where this list might escape.
+ */
+ final Set<Element> escapingElements = new Set<Element>();
+
+ /**
+ * A set of selectors that both use and update the list, for example
+ * [: list[0]++; :] or [: list[0] |= 42; :].
+ */
+ final Set<Selector> constraints = new Set<Selector>();
+
+ /**
+ * A cache of setters that were already seen. Caching these
+ * selectors avoid the filtering done in [addSettersToAnalysis].
+ */
+ final Set<Selector> seenSetterSelectors = new Set<Selector>();
+
+ static const int MAX_ANALYSIS_COUNT = 11;
+
+ TypeMask potentialType;
+ bool continueAnalyzing = true;
+
+ TracerForConcreteContainer(ContainerTypeMask mask,
+ this.tracer,
+ this.compiler,
+ this.inferrer)
+ : analyzedNode = mask.allocationNode,
+ startElement = mask.allocationElement;
+
+ TypeMask run() {
+ int analysisCount = 0;
+ workList.add(startElement);
+ while (!workList.isEmpty) {
+ if (workList.length + analysisCount > MAX_ANALYSIS_COUNT) {
+ bailout('Too many users');
+ break;
+ }
+ Element currentElement = workList.removeLast().implementation;
+ new ContainerTracerVisitor(currentElement, this).run();
+ if (!continueAnalyzing) break;
+ analysisCount++;
+ }
+
+ if (!continueAnalyzing) return inferrer.dynamicType;
+
+ // [potentialType] can be null if we did not find any instruction
+ // that adds elements to the list.
+ if (potentialType == null) return new TypeMask.empty();
+
+ potentialType = potentialType.nullable();
+ // Walk over the found constraints and update the type according
+ // to the selectors of these constraints.
+ for (Selector constraint in constraints) {
+ assert(constraint.isOperator());
+ constraint = new TypedSelector(potentialType, constraint);
+ potentialType = potentialType.union(
+ inferrer.getTypeOfSelector(constraint), compiler);
+ }
+ if (_VERBOSE) {
+ print('$potentialType for $analyzedNode');
+ }
+ return potentialType;
+ }
+
+
+ void unionPotentialTypeWith(TypeMask newType) {
+ assert(newType != null);
+ potentialType = potentialType == null
+ ? newType
+ : newType.union(potentialType, compiler);
+ if (potentialType == inferrer.dynamicType) {
+ bailout('Moved to dynamic');
+ }
+ }
+
+ void addEscapingElement(element) {
+ element = element.implementation;
+ if (escapingElements.contains(element)) return;
+ escapingElements.add(element);
+ if (element.isField() || element.isGetter() || element.isFunction()) {
+ for (Element e in inferrer.getCallersOf(element)) {
+ addElementToAnalysis(e);
+ }
+ } else if (element.isParameter()) {
+ addElementToAnalysis(element.enclosingElement);
+ } else if (element.isFieldParameter()) {
+ addEscapingElement(element.fieldElement);
+ }
+ }
+
+ void addSettersToAnalysis(Selector selector) {
+ assert(selector.isSetter());
+ if (seenSetterSelectors.contains(selector)) return;
+ seenSetterSelectors.add(selector);
+ for (var e in compiler.world.allFunctions.filter(selector)) {
+ e = e.implementation;
+ if (e.isField()) {
+ addEscapingElement(e);
+ } else {
+ FunctionSignature signature = e.computeSignature(compiler);
+ signature.forEachRequiredParameter((Element e) {
+ addEscapingElement(e);
+ });
+ }
+ }
+ }
+
+ void addElementToAnalysis(Element element) {
+ workList.add(element);
+ }
+
+ TypeMask bailout(String reason) {
+ if (_VERBOSE) {
+ print('Bailout on $analyzedNode $startElement because of $reason');
+ }
+ continueAnalyzing = false;
+ return inferrer.dynamicType;
+ }
+
+ bool couldBeTheList(resolved) {
+ if (resolved is Selector) {
+ return escapingElements.any((e) {
+ return e.isInstanceMember()
+ && (e.isField() || e.isFunction())
+ && resolved.applies(e, compiler);
+ });
+ } else if (resolved is Node) {
+ return analyzedNode == resolved;
+ } else {
+ assert(resolved is Element);
+ return escapingElements.contains(resolved);
+ }
+ }
+
+ void recordConstraint(Selector selector) {
+ constraints.add(selector);
+ }
+}
+
+class ContainerTracerVisitor extends InferrerVisitor {
+ final Element analyzedElement;
+ final TracerForConcreteContainer tracer;
+ ContainerTracerVisitor(element, tracer)
+ : super(element, tracer.inferrer, tracer.compiler),
+ this.analyzedElement = element,
+ this.tracer = tracer;
+
+ bool escaping = false;
+ bool visitingClosure = false;
+ bool visitingInitializers = false;
+
+ void run() {
+ compiler.withCurrentElement(analyzedElement, () {
+ visit(analyzedElement.parseNode(compiler));
+ });
+ }
+
+ /**
+ * Executes [f] and returns whether it triggered the list to escape.
+ */
+ bool visitAndCatchEscaping(Function f) {
+ bool oldEscaping = escaping;
+ escaping = false;
+ f();
+ bool foundEscaping = escaping;
+ escaping = oldEscaping;
+ return foundEscaping;
+ }
+
+ /**
+ * Visits the [arguments] of [callee], and records the parameters
+ * that could hold the container as escaping.
+ *
+ * Returns whether the container escaped.
+ */
+ bool visitArguments(Link<Node> arguments, /* Element or Selector */ callee) {
+ List<int> indices = [];
+ int index = 0;
+ for (Node node in arguments) {
+ if (visitAndCatchEscaping(() { visit(node); })) {
+ indices.add(index);
+ }
+ index++;
+ }
+ if (!indices.isEmpty) {
+ Iterable<Element> callees;
+ if (callee is Element) {
+ callees = [callee];
+ } else {
+ assert(callee is Selector);
+ callees = compiler.world.allFunctions.filter(callee);
+ }
+ for (var e in callees) {
+ e = e.implementation;
+ if (e.isField()) {
+ tracer.bailout('Passed to a closure');
+ break;
+ }
+ FunctionSignature signature = e.computeSignature(compiler);
+ index = 0;
+ int parameterIndex = 0;
+ signature.forEachRequiredParameter((Element parameter) {
+ if (index < indices.length && indices[index] == parameterIndex) {
+ tracer.addEscapingElement(parameter);
+ index++;
+ }
+ parameterIndex++;
+ });
+ if (index != indices.length) {
+ tracer.bailout('Used in a named parameter or closure');
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ TypeMask visitFunctionExpression(FunctionExpression node) {
+ bool oldVisitingClosure = visitingClosure;
+ FunctionElement function = elements[node];
+ FunctionSignature signature = function.computeSignature(compiler);
+ signature.forEachParameter((element) {
+ locals.update(element, inferrer.getTypeOfElement(element));
+ });
+ visitingClosure = function != analyzedElement;
+ bool oldVisitingInitializers = visitingInitializers;
+ visitingInitializers = true;
+ visit(node.initializers);
+ visitingInitializers = oldVisitingInitializers;
+ visit(node.body);
+ visitingClosure = oldVisitingClosure;
+
+ return inferrer.functionType;
+ }
+
+ TypeMask visitLiteralList(LiteralList node) {
+ if (node.isConst()) return inferrer.constListType;
+ if (tracer.couldBeTheList(node)) {
+ escaping = true;
+ for (Node element in node.elements.nodes) {
+ tracer.unionPotentialTypeWith(visit(element));
+ }
+ } else {
+ node.visitChildren(this);
+ }
+ return inferrer.growableListType;
+ }
+
+ // TODO(ngeoffray): Try to move the following two methods in
+ // [InferrerVisitor].
+ TypeMask visitCascadeReceiver(CascadeReceiver node) {
+ return visit(node.expression);
+ }
+
+ TypeMask visitCascade(Cascade node) {
+ Send send = node.expression;
+ TypeMask result;
+ bool isReceiver = visitAndCatchEscaping(() {
+ result = visit(send.receiver);
+ });
+ if (send.asSendSet() != null) {
+ handleSendSet(send, isReceiver);
+ } else {
+ handleDynamicSend(send, isReceiver);
+ }
+ if (isReceiver) {
+ escaping = true;
+ }
+ return result;
+ }
+
+ TypeMask visitSendSet(SendSet node) {
+ bool isReceiver = visitAndCatchEscaping(() {
+ visit(node.receiver);
+ });
+ return handleSendSet(node, isReceiver);
+ }
+
+ TypeMask handleSendSet(SendSet node, bool isReceiver) {
+ TypeMask rhsType;
+ TypeMask indexType;
+
+ Selector getterSelector =
+ elements.getGetterSelectorInComplexSendSet(node);
+ Selector operatorSelector =
+ elements.getOperatorSelectorInComplexSendSet(node);
+ Selector setterSelector = elements.getSelector(node);
+
+ String op = node.assignmentOperator.source.stringValue;
+ bool isIncrementOrDecrement = op == '++' || op == '--';
+ bool isIndexEscaping = false;
+ bool isValueEscaping = false;
+ if (isIncrementOrDecrement) {
+ rhsType = inferrer.intType;
+ if (node.isIndex) {
+ isIndexEscaping = visitAndCatchEscaping(() {
+ indexType = visit(node.arguments.head);
+ });
+ }
+ } else if (node.isIndex) {
+ isIndexEscaping = visitAndCatchEscaping(() {
+ indexType = visit(node.arguments.head);
+ });
+ isValueEscaping = visitAndCatchEscaping(() {
+ rhsType = visit(node.arguments.tail.head);
+ });
+ } else {
+ isValueEscaping = visitAndCatchEscaping(() {
+ rhsType = visit(node.arguments.head);
+ });
+ }
+
+ Element element = elements[node];
+
+ if (node.isIndex) {
+ if (isReceiver) {
+ if (op == '=') {
+ tracer.unionPotentialTypeWith(rhsType);
+ } else {
+ tracer.recordConstraint(operatorSelector);
+ }
+ } else if (isIndexEscaping || isValueEscaping) {
+ // If the index or value is escaping, iterate over all
+ // potential targets, and mark their parameter as escaping.
+ for (var e in compiler.world.allFunctions.filter(setterSelector)) {
+ e = e.implementation;
+ FunctionSignature signature = e.computeSignature(compiler);
+ int index = 0;
+ signature.forEachRequiredParameter((Element parameter) {
+ if (index == 0 && isIndexEscaping) {
+ tracer.addEscapingElement(parameter);
+ }
+ if (index == 1 && isValueEscaping) {
+ tracer.addEscapingElement(parameter);
+ }
+ index++;
+ });
+ }
+ }
+ } else if (isReceiver) {
+ if (setterSelector.name == const SourceString('length')) {
+ // Changing the length.
+ tracer.unionPotentialTypeWith(inferrer.nullType);
+ }
+ } else if (isValueEscaping) {
+ if (element != null
+ && element.isField()
+ && setterSelector == null
+ && !visitingInitializers) {
+ // Initializer at declaration of a field.
+ assert(analyzedElement.isField());
+ tracer.addEscapingElement(analyzedElement);
+ } else if (element != null
+ && (!element.isInstanceMember() || visitingInitializers)) {
+ // A local, a static element, or a field in an initializer.
+ tracer.addEscapingElement(element);
+ } else {
+ tracer.addSettersToAnalysis(setterSelector);
+ }
+ }
+
+ if (Elements.isLocal(element)) {
+ locals.update(element, rhsType);
+ }
+
+ if (node.isPostfix) {
+ // We don't check if [getterSelector] could be the container because
+ // a list++ will always throw.
+ return inferrer.getTypeOfSelector(getterSelector);
+ } else if (op != '=') {
+ // We don't check if [getterSelector] could be the container because
+ // a list += 42 will always throw.
+ return inferrer.getTypeOfSelector(operatorSelector);
+ } else {
+ if (isValueEscaping) {
+ escaping = true;
+ }
+ return rhsType;
+ }
+ }
+
+ TypeMask visitSuperSend(Send node) {
+ Element element = elements[node];
+ if (!node.isPropertyAccess) {
+ visitArguments(node.arguments, element);
+ }
+
+ if (tracer.couldBeTheList(element)) {
+ escaping = true;
+ }
+
+ if (element.isField()) {
+ return inferrer.getTypeOfElement(element);
+ } else if (element.isFunction()) {
+ return inferrer.getReturnTypeOfElement(element);
+ } else {
+ return inferrer.dynamicType;
+ }
+ }
+
+ TypeMask visitStaticSend(Send node) {
+ Element element = elements[node];
+ bool isEscaping = visitArguments(node.arguments, element);
+
+ if (element.isForeign(compiler)) {
+ if (isEscaping) return tracer.bailout('Used in a JS');
+ }
+
+ if (tracer.couldBeTheList(element)) {
+ escaping = true;
+ }
+
+ if (Elements.isGrowableListConstructorCall(element, node, compiler)) {
+ if (tracer.couldBeTheList(node)) {
+ escaping = true;
+ }
+ return inferrer.growableListType;
+ } else if (Elements.isFixedListConstructorCall(element, node, compiler)) {
+ if (tracer.couldBeTheList(node)) {
+ escaping = true;
+ }
+ return inferrer.fixedListType;
+ } else if (element.isFunction() || element.isConstructor()) {
+ return inferrer.getReturnTypeOfElement(element);
+ } else {
+ // Closure call or unresolved.
+ return inferrer.dynamicType;
+ }
+ }
+
+ TypeMask visitGetterSend(Send node) {
+ Element element = elements[node];
+ Selector selector = elements.getSelector(node);
+ if (Elements.isStaticOrTopLevelField(element)) {
+ if (tracer.couldBeTheList(element)) {
+ escaping = true;
+ }
+ return inferrer.getTypeOfElement(element);
+ } else if (Elements.isInstanceSend(node, elements)) {
+ return visitDynamicSend(node);
+ } else if (Elements.isStaticOrTopLevelFunction(element)) {
+ return inferrer.functionType;
+ } else if (Elements.isErroneousElement(element)) {
+ return inferrer.dynamicType;
+ } else if (Elements.isLocal(element)) {
+ if (tracer.couldBeTheList(element)) {
+ escaping = true;
+ }
+ return locals.use(element);
+ } else {
+ node.visitChildren(this);
+ return inferrer.dynamicType;
+ }
+ }
+
+ TypeMask visitClosureSend(Send node) {
+ assert(node.receiver == null);
+ visit(node.selector);
+ bool isEscaping =
+ visitArguments(node.arguments, elements.getSelector(node));
+
+ if (isEscaping) return tracer.bailout('Passed to a closure');
+ return inferrer.dynamicType;
+ }
+
+ TypeMask visitDynamicSend(Send node) {
+ bool isReceiver = visitAndCatchEscaping(() {
+ visit(node.receiver);
+ });
+ return handleDynamicSend(node, isReceiver);
+ }
+
+ TypeMask handleDynamicSend(Send node, bool isReceiver) {
+ Selector selector = elements.getSelector(node);
+ String selectorName = selector.name.slowToString();
+ if (isReceiver && !okSelectorsSet.contains(selectorName)) {
+ if (selector.isCall()
+ && (selectorName == 'add' || selectorName == 'insert')) {
+ TypeMask argumentType;
+ if (node.arguments.isEmpty
+ || (selectorName == 'insert' && node.arguments.tail.isEmpty)) {
+ return tracer.bailout('Invalid "add" or "insert" call on a list');
+ }
+ bool isEscaping = visitAndCatchEscaping(() {
+ argumentType = visit(node.arguments.head);
+ if (selectorName == 'insert') {
+ argumentType = visit(node.arguments.tail.head);
+ }
+ });
+ if (isEscaping) {
+ return tracer.bailout('List containing itself');
+ }
+ tracer.unionPotentialTypeWith(argumentType);
+ } else {
+ return tracer.bailout('Send with the node as receiver $node');
+ }
+ } else if (!node.isPropertyAccess) {
+ visitArguments(node.arguments, selector);
+ }
+ if (tracer.couldBeTheList(selector)) {
+ escaping = true;
+ }
+ return inferrer.getTypeOfSelector(selector);
+ }
+
+ TypeMask visitReturn(Return node) {
+ if (node.expression == null) {
+ return inferrer.nullType;
+ }
+
+ TypeMask type;
+ bool isEscaping = visitAndCatchEscaping(() {
+ type = visit(node.expression);
+ });
+
+ if (isEscaping) {
+ if (visitingClosure) {
+ tracer.bailout('Return from closure');
+ } else {
+ tracer.addEscapingElement(analyzedElement);
+ }
+ }
+ return type;
+ }
+
+ TypeMask visitForIn(ForIn node) {
+ visit(node.expression);
+ Selector iteratorSelector = elements.getIteratorSelector(node);
+ Selector currentSelector = elements.getCurrentSelector(node);
+
+ TypeMask iteratorType = inferrer.getTypeOfSelector(iteratorSelector);
+ TypeMask currentType = inferrer.getTypeOfSelector(currentSelector);
+
+ // We nullify the type in case there is no element in the
+ // iterable.
+ currentType = currentType.nullable();
+
+ Node identifier = node.declaredIdentifier;
+ Element element = elements[identifier];
+ if (Elements.isLocal(element)) {
+ locals.update(element, currentType);
+ }
+
+ return handleLoop(node, () {
+ visit(node.body);
+ });
+ }
+}
diff --git a/sdk/lib/_internal/compiler/implementation/types/container_type_mask.dart b/sdk/lib/_internal/compiler/implementation/types/container_type_mask.dart
new file mode 100644
index 0000000..3684755
--- /dev/null
+++ b/sdk/lib/_internal/compiler/implementation/types/container_type_mask.dart
@@ -0,0 +1,138 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of types;
+
+/// A holder for an element type. We define a special class for it so
+/// that nullable ContainerTypeMask and non-nullable ContainerTypeMask
+/// share the same [ElementTypeHolder].
+class ElementTypeHolder {
+ // This field will be set after global analysis.
+ TypeMask elementType;
+}
+
+/// A [ContainerTypeMask] is a [TypeMask] for a specific allocation
+/// site of a container (currently only List) that will get specialized
+/// once the [ListTracer] phase finds an element type for it.
+class ContainerTypeMask implements TypeMask {
+
+ // The flat version of a [ContainerTypeMask] is the container type
+ // (for example List).
+ final FlatTypeMask asFlat;
+
+ // The [Node] where this type mask was created.
+ final Node allocationNode;
+
+ // The [Element] where this type mask was created.
+ final Element allocationElement;
+
+ // A holder for the element type. Shared between all
+ // [ContainerTypeMask] for the same node.
+ final ElementTypeHolder holder;
+
+ TypeMask get elementType => holder.elementType;
+ void set elementType(TypeMask mask) {
+ holder.elementType = mask;
+ }
+
+ ContainerTypeMask(this.asFlat,
+ this.allocationNode,
+ this.allocationElement,
+ [holder])
+ : this.holder = (holder == null) ? new ElementTypeHolder() : holder;
+
+ TypeMask nullable() {
+ return isNullable
+ ? this
+ : new ContainerTypeMask(asFlat.nullable(),
+ allocationNode,
+ allocationElement,
+ holder);
+ }
+
+ TypeMask nonNullable() {
+ return isNullable
+ ? new ContainerTypeMask(asFlat.nonNullable(),
+ allocationNode,
+ allocationElement,
+ holder)
+ : this;
+ }
+
+ TypeMask simplify(Compiler compiler) => this;
+
+ bool get isEmpty => false;
+ bool get isNullable => asFlat.isNullable;
+ bool get isExact => true;
+ bool get isUnion => false;
+ bool get isContainer => true;
+
+ bool containsOnlyInt(Compiler compiler) => false;
+ bool containsOnlyDouble(Compiler compiler) => false;
+ bool containsOnlyNum(Compiler compiler) => false;
+ bool containsOnlyNull(Compiler compiler) => false;
+ bool containsOnlyBool(Compiler compiler) => false;
+ bool containsOnlyString(Compiler compiler) => false;
+ bool containsOnly(ClassElement element) {
+ return asFlat.containsOnly(element);
+ }
+
+ bool satisfies(ClassElement cls, Compiler compiler) {
+ return asFlat.satisfies(cls, compiler);
+ }
+
+ bool contains(DartType type, Compiler compiler) {
+ return asFlat.contains(type, compiler);
+ }
+
+ bool containsAll(Compiler compiler) => false;
+
+ ClassElement singleClass(Compiler compiler) {
+ return asFlat.singleClass(compiler);
+ }
+
+ Iterable<ClassElement> containedClasses(Compiler compiler) {
+ return asFlat.containedClasses(compiler);
+ }
+
+ TypeMask union(other, Compiler compiler) {
+ if (other.isContainer
+ && other.allocationNode == this.allocationNode) {
+ return other.isNullable ? other : this;
+ } else if (other.isEmpty) {
+ return other.isNullable ? this.nullable() : this;
+ }
+ return asFlat.union(other, compiler);
+ }
+
+ TypeMask intersection(TypeMask other, Compiler compiler) {
+ TypeMask flatIntersection = asFlat.intersection(other, compiler);
+ if (flatIntersection.isEmpty) return flatIntersection;
+ return flatIntersection.isNullable
+ ? nullable()
+ : nonNullable();
+ }
+
+ bool willHit(Selector selector, Compiler compiler) {
+ return asFlat.willHit(selector, compiler);
+ }
+
+ bool canHit(Element element, Selector selector, Compiler compiler) {
+ return asFlat.canHit(element, selector, compiler);
+ }
+
+ Element locateSingleElement(Selector selector, Compiler compiler) {
+ return asFlat.locateSingleElement(selector, compiler);
+ }
+
+ bool operator==(other) {
+ if (other is! ContainerTypeMask) return false;
+ return allocationNode == other.allocationNode
+ && isNullable == other.isNullable;
+ }
+
+ String toString() {
+ return 'Container mask: $elementType';
+ }
+}
diff --git a/sdk/lib/_internal/compiler/implementation/types/flat_type_mask.dart b/sdk/lib/_internal/compiler/implementation/types/flat_type_mask.dart
index a318d0e..c96f28d 100644
--- a/sdk/lib/_internal/compiler/implementation/types/flat_type_mask.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/flat_type_mask.dart
@@ -63,6 +63,7 @@
bool get isExact => (flags >> 1) == EXACT;
bool get isNullable => (flags & 1) != 0;
bool get isUnion => false;
+ bool get isContainer => false;
// TODO(kasperl): Get rid of these. They should not be a visible
// part of the implementation because they make it hard to add
diff --git a/sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart b/sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart
index 68aa7ce..75e35e1 100644
--- a/sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart
@@ -10,7 +10,7 @@
{bool isNullable: true}) {
if (annotation.isDynamic) return type;
if (annotation.isMalformed) return type;
- if (annotation.isVoid) return compiler.typesTask.typesInferrer.nullType;
+ if (annotation.isVoid) return compiler.typesTask.nullType;
if (annotation.element == compiler.objectClass) return type;
TypeMask otherType;
if (annotation.kind == TypeKind.TYPEDEF
@@ -34,8 +34,7 @@
TypeMask computeLUB(TypeMask firstType,
TypeMask secondType,
Compiler compiler) {
- TypesInferrer inferrer = compiler.typesTask.typesInferrer;
- TypeMask dynamicType = inferrer.dynamicType;
+ TypeMask dynamicType = compiler.typesTask.dynamicType;
if (firstType == null) {
return secondType;
} else if (secondType == dynamicType) {
@@ -54,7 +53,8 @@
* Placeholder for inferred types of local variables.
*/
class LocalsHandler {
- final InternalSimpleTypesInferrer inferrer;
+ final Compiler compiler;
+ final TypesInferrer inferrer;
final Map<Element, TypeMask> locals;
final Map<Element, Element> capturedAndBoxed;
final Map<Element, TypeMask> fieldsInitializedInConstructor;
@@ -67,7 +67,7 @@
return seenReturn || seenBreakOrContinue;
}
- LocalsHandler(this.inferrer)
+ LocalsHandler(this.inferrer, this.compiler)
: locals = new Map<Element, TypeMask>(),
capturedAndBoxed = new Map<Element, Element>(),
fieldsInitializedInConstructor = new Map<Element, TypeMask>(),
@@ -81,6 +81,7 @@
other.fieldsInitializedInConstructor),
inTryBlock = other.inTryBlock || inTryBlock,
inferrer = other.inferrer,
+ compiler = other.compiler,
isThisExposed = other.isThisExposed;
TypeMask use(Element local) {
@@ -92,7 +93,6 @@
void update(Element local, TypeMask type) {
assert(type != null);
- Compiler compiler = inferrer.compiler;
if (compiler.trustTypeAnnotations || compiler.enableTypeAssertions) {
type = narrowType(type, local.computeType(compiler), compiler);
}
@@ -139,7 +139,7 @@
} else if (!isCaptured && other.aborts && discardIfAborts) {
// Don't do anything.
} else {
- TypeMask type = computeLUB(oldType, otherType, inferrer.compiler);
+ TypeMask type = computeLUB(oldType, otherType, compiler);
if (type != oldType) changed = true;
locals[local] = type;
}
@@ -175,7 +175,7 @@
toRemove.add(element);
} else {
fieldsInitializedInConstructor[element] =
- computeLUB(type, otherType, inferrer.compiler);
+ computeLUB(type, otherType, compiler);
}
});
// Remove fields that were not initialized in [other].
@@ -197,7 +197,9 @@
abstract class InferrerVisitor extends ResolvedVisitor<TypeMask> {
final Element analyzedElement;
- final InternalSimpleTypesInferrer inferrer;
+ // Subclasses know more about this field. Typing it dynamic to avoid
+ // warnings.
+ final /* TypesInferrer */ inferrer;
final Compiler compiler;
final Map<TargetElement, List<LocalsHandler>> breaksFor =
new Map<TargetElement, List<LocalsHandler>>();
@@ -217,10 +219,14 @@
InferrerVisitor(Element analyzedElement,
this.inferrer,
Compiler compiler,
- this.locals)
+ [LocalsHandler handler])
: this.compiler = compiler,
this.analyzedElement = analyzedElement,
- super(compiler.enqueuer.resolution.getCachedElements(analyzedElement));
+ super(compiler.enqueuer.resolution.getCachedElements(analyzedElement)) {
+ locals = (handler == null)
+ ? new LocalsHandler(inferrer, compiler)
+ : handler;
+ }
TypeMask visitSendSet(SendSet node);
@@ -252,6 +258,7 @@
}
TypeMask visitFunctionExpression(FunctionExpression node) {
+ node.visitChildren(this);
return inferrer.functionType;
}
@@ -320,11 +327,11 @@
if (_thisType != null) return _thisType;
ClassElement cls = outermostElement.getEnclosingClass();
if (compiler.world.isUsedAsMixin(cls)) {
- return _thisType = new TypeMask.nonNullSubtype(inferrer.rawTypeOf(cls));
+ return _thisType = new TypeMask.nonNullSubtype(cls.rawType);
} else if (compiler.world.hasAnySubclass(cls)) {
- return _thisType = new TypeMask.nonNullSubclass(inferrer.rawTypeOf(cls));
+ return _thisType = new TypeMask.nonNullSubclass(cls.rawType);
} else {
- return _thisType = new TypeMask.nonNullExact(inferrer.rawTypeOf(cls));
+ return _thisType = new TypeMask.nonNullExact(cls.rawType);
}
}
@@ -332,7 +339,7 @@
TypeMask get superType {
if (_superType != null) return _superType;
return _superType = new TypeMask.nonNullExact(
- inferrer.rawTypeOf(outermostElement.getEnclosingClass().superclass));
+ outermostElement.getEnclosingClass().superclass.rawType);
}
TypeMask visitIdentifier(Identifier node) {
@@ -362,7 +369,7 @@
Element element = elements[node.receiver];
TypeMask existing = locals.use(element);
TypeMask newType = narrowType(
- existing, type, inferrer.compiler, isNullable: false);
+ existing, type, compiler, isNullable: false);
locals.update(element, newType);
}
}
@@ -408,7 +415,7 @@
} else if (const SourceString("as") == op.source) {
TypeMask receiverType = visit(node.receiver);
DartType type = elements.getType(node.arguments.head);
- return narrowType(receiverType, type, inferrer.compiler);
+ return narrowType(receiverType, type, compiler);
} else if (node.isParameterCheck) {
node.visitChildren(this);
return inferrer.boolType;
diff --git a/sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart b/sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
index 4a9b948..634a542 100644
--- a/sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
@@ -13,7 +13,8 @@
import '../native_handler.dart' as native;
import '../tree/tree.dart';
import '../util/util.dart' show Link;
-import 'types.dart' show TypesInferrer, FlatTypeMask, TypeMask;
+import 'types.dart'
+ show TypesInferrer, FlatTypeMask, TypeMask, ContainerTypeMask;
// BUG(8802): There's a bug in the analyzer that makes the re-export
// of Selector from dart2jslib.dart fail. For now, we work around that
@@ -111,21 +112,21 @@
compiler = compiler,
internal = new InternalSimpleTypesInferrer(compiler, OPTIMISTIC);
- TypeMask get dynamicType => internal.dynamicType;
- TypeMask get nullType => internal.nullType;
- TypeMask get intType => internal.intType;
- TypeMask get doubleType => internal.doubleType;
- TypeMask get numType => internal.numType;
- TypeMask get boolType => internal.boolType;
- TypeMask get functionType => internal.functionType;
- TypeMask get listType => internal.listType;
- TypeMask get constListType => internal.constListType;
- TypeMask get fixedListType => internal.fixedListType;
- TypeMask get growableListType => internal.growableListType;
- TypeMask get mapType => internal.mapType;
- TypeMask get constMapType => internal.constMapType;
- TypeMask get stringType => internal.stringType;
- TypeMask get typeType => internal.typeType;
+ TypeMask get dynamicType => compiler.typesTask.dynamicType;
+ TypeMask get nullType => compiler.typesTask.nullType;
+ TypeMask get intType => compiler.typesTask.intType;
+ TypeMask get doubleType => compiler.typesTask.doubleType;
+ TypeMask get numType => compiler.typesTask.numType;
+ TypeMask get boolType => compiler.typesTask.boolType;
+ TypeMask get functionType => compiler.typesTask.functionType;
+ TypeMask get listType => compiler.typesTask.listType;
+ TypeMask get constListType => compiler.typesTask.constListType;
+ TypeMask get fixedListType => compiler.typesTask.fixedListType;
+ TypeMask get growableListType => compiler.typesTask.growableListType;
+ TypeMask get mapType => compiler.typesTask.mapType;
+ TypeMask get constMapType => compiler.typesTask.constMapType;
+ TypeMask get stringType => compiler.typesTask.stringType;
+ TypeMask get typeType => compiler.typesTask.typeType;
TypeMask getReturnTypeOfElement(Element element) {
if (compiler.disableTypeInference) return dynamicType;
@@ -144,6 +145,14 @@
return internal.getTypeOfSelector(selector);
}
+ Iterable<Element> getCallersOf(Element element) {
+ if (compiler.disableTypeInference) throw "Don't use me";
+ Iterable<Element> result = internal.callersOf[element];
+ return result == null
+ ? internal.callersOf[element] = const <Element>[]
+ : result;
+ }
+
bool analyzeMain(Element element) {
if (compiler.disableTypeInference) return true;
bool result = internal.analyzeMain(element);
@@ -154,14 +163,24 @@
internal = new InternalSimpleTypesInferrer(compiler, PESSIMISTIC);
return internal.analyzeMain(element);
}
+
+ void clear() {
+ internal.clear();
+ }
}
class InternalSimpleTypesInferrer extends TypesInferrer {
/**
+ * Maps a node to its type. Currently used for computing element
+ * types of lists.
+ */
+ final Map<Node, TypeMask> concreteTypes = new Map<Node, TypeMask>();
+
+ /**
* Maps an element to its callers.
*/
- final Map<Element, Set<Element>> callersOf =
- new Map<Element, Set<Element>>();
+ final Map<Element, Iterable<Element>> callersOf =
+ new Map<Element, Iterable<Element>>();
/**
* Maps an element to its return type.
@@ -241,23 +260,22 @@
int optimismState;
- TypeMask dynamicType;
bool isDynamicType(TypeMask type) => identical(type, dynamicType);
-
- TypeMask nullType;
- TypeMask intType;
- TypeMask doubleType;
- TypeMask numType;
- TypeMask boolType;
- TypeMask functionType;
- TypeMask listType;
- TypeMask constListType;
- TypeMask fixedListType;
- TypeMask growableListType;
- TypeMask mapType;
- TypeMask constMapType;
- TypeMask stringType;
- TypeMask typeType;
+ TypeMask get dynamicType => compiler.typesTask.dynamicType;
+ TypeMask get nullType => compiler.typesTask.nullType;
+ TypeMask get intType => compiler.typesTask.intType;
+ TypeMask get doubleType => compiler.typesTask.doubleType;
+ TypeMask get numType => compiler.typesTask.numType;
+ TypeMask get boolType => compiler.typesTask.boolType;
+ TypeMask get functionType => compiler.typesTask.functionType;
+ TypeMask get listType => compiler.typesTask.listType;
+ TypeMask get constListType => compiler.typesTask.constListType;
+ TypeMask get fixedListType => compiler.typesTask.fixedListType;
+ TypeMask get growableListType => compiler.typesTask.growableListType;
+ TypeMask get mapType => compiler.typesTask.mapType;
+ TypeMask get constMapType => compiler.typesTask.constMapType;
+ TypeMask get stringType => compiler.typesTask.stringType;
+ TypeMask get typeType => compiler.typesTask.typeType;
/**
* These are methods that are expected to return only bool. We optimistically
@@ -308,7 +326,6 @@
* found as reachable. Returns whether it succeeded.
*/
bool analyzeMain(Element element) {
- initializeTypes();
buildWorkQueue();
int analyzed = 0;
compiler.progress.reset();
@@ -345,7 +362,6 @@
}
} while (!workSet.isEmpty);
dump();
- clear();
return true;
}
@@ -377,17 +393,7 @@
* defined in the context of [owner].
*/
TypeMask getTypeOfNode(Element owner, Node node) {
- var elements = compiler.enqueuer.resolution.resolvedElements[owner];
- // TODO(ngeoffray): Not sure why the resolver would put a null
- // mapping.
- if (elements == null) return null;
- Selector selector = elements.getSelector(node);
- // TODO(ngeoffray): Should the builder call this method with a
- // SendSet?
- if (selector == null || selector.isSetter() || selector.isIndexSet()) {
- return null;
- }
- return getNonNullType(typeOfSelector(selector));
+ return getNonNullType(concreteTypes[node]);
}
void checkAnalyzedAll() {
@@ -496,42 +502,6 @@
return cls.rawType;
}
- void initializeTypes() {
- nullType = new TypeMask.empty();
-
- Backend backend = compiler.backend;
- intType = new TypeMask.nonNullExact(
- rawTypeOf(backend.intImplementation));
- doubleType = new TypeMask.nonNullExact(
- rawTypeOf(backend.doubleImplementation));
- numType = new TypeMask.nonNullSubclass(
- rawTypeOf(backend.numImplementation));
- stringType = new TypeMask.nonNullExact(
- rawTypeOf(backend.stringImplementation));
- boolType = new TypeMask.nonNullExact(
- rawTypeOf(backend.boolImplementation));
-
- listType = new TypeMask.nonNullExact(
- rawTypeOf(backend.listImplementation));
- constListType = new TypeMask.nonNullExact(
- rawTypeOf(backend.constListImplementation));
- fixedListType = new TypeMask.nonNullExact(
- rawTypeOf(backend.fixedListImplementation));
- growableListType = new TypeMask.nonNullExact(
- rawTypeOf(backend.growableListImplementation));
-
- mapType = new TypeMask.nonNullSubtype(
- rawTypeOf(backend.mapImplementation));
- constMapType = new TypeMask.nonNullSubtype(
- rawTypeOf(backend.constMapImplementation));
- functionType = new TypeMask.nonNullSubtype(
- rawTypeOf(backend.functionImplementation));
- typeType = new TypeMask.nonNullExact(
- rawTypeOf(backend.typeImplementation));
-
- dynamicType = new TypeMask.subclass(rawTypeOf(compiler.objectClass));
- }
-
dump() {
int interestingTypes = 0;
returnTypeOf.forEach((Element element, TypeMask type) {
@@ -815,6 +785,12 @@
assert(element.isGetter());
return returnTypeOfElement(element);
}
+ } else if (selector.isIndex()
+ && selector.mask != null
+ && selector.mask.isContainer) {
+ ContainerTypeMask mask = selector.mask;
+ TypeMask elementType = mask.elementType;
+ return elementType == null ? dynamicType : elementType;
} else {
return returnTypeOfElement(element);
}
@@ -1288,7 +1264,6 @@
Element outermostElement =
element.getOutermostEnclosingMemberOrTopLevel().implementation;
assert(outermostElement != null);
- handler = handler != null ? handler : new LocalsHandler(inferrer);
return new SimpleTypeInferrerVisitor.internal(
element, outermostElement, inferrer, compiler, handler);
}
@@ -1451,6 +1426,14 @@
return inferrer.functionType;
}
+ TypeMask visitLiteralList(LiteralList node) {
+ node.visitChildren(this);
+ if (node.isConst()) return inferrer.constListType;
+ return inferrer.concreteTypes.putIfAbsent(
+ node, () => new ContainerTypeMask(
+ inferrer.growableListType, node, outermostElement));
+ }
+
bool isThisOrSuper(Node node) => node.isThis() || node.isSuper();
void checkIfExposesThis(Selector selector) {
@@ -1707,9 +1690,13 @@
handleStaticSend(node, selector, element, arguments);
if (Elements.isGrowableListConstructorCall(element, node, compiler)) {
- return inferrer.growableListType;
+ return inferrer.concreteTypes.putIfAbsent(
+ node, () => new ContainerTypeMask(
+ inferrer.growableListType, node, outermostElement));
} else if (Elements.isFixedListConstructorCall(element, node, compiler)) {
- return inferrer.fixedListType;
+ return inferrer.concreteTypes.putIfAbsent(
+ node, () => new ContainerTypeMask(
+ inferrer.fixedListType, node, outermostElement));
} else if (element.isFunction() || element.isConstructor()) {
return inferrer.returnTypeOfElement(element);
} else {
diff --git a/sdk/lib/_internal/compiler/implementation/types/type_mask.dart b/sdk/lib/_internal/compiler/implementation/types/type_mask.dart
index c00b4bd..e53cc0a 100644
--- a/sdk/lib/_internal/compiler/implementation/types/type_mask.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/type_mask.dart
@@ -48,6 +48,7 @@
bool get isNullable;
bool get isExact;
bool get isUnion;
+ bool get isContainer;
bool containsOnlyInt(Compiler compiler);
bool containsOnlyDouble(Compiler compiler);
diff --git a/sdk/lib/_internal/compiler/implementation/types/types.dart b/sdk/lib/_internal/compiler/implementation/types/types.dart
index 60f3641..d260746 100644
--- a/sdk/lib/_internal/compiler/implementation/types/types.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/types.dart
@@ -17,6 +17,7 @@
import '../dart_types.dart';
part 'concrete_types_inferrer.dart';
+part 'container_type_mask.dart';
part 'flat_type_mask.dart';
part 'type_mask.dart';
part 'union_type_mask.dart';
@@ -30,22 +31,6 @@
TypeMask getTypeOfElement(Element element);
TypeMask getTypeOfNode(Element owner, Node node);
TypeMask getTypeOfSelector(Selector selector);
-
- TypeMask get dynamicType;
- TypeMask get nullType;
- TypeMask get intType;
- TypeMask get doubleType;
- TypeMask get numType;
- TypeMask get boolType;
- TypeMask get functionType;
- TypeMask get listType;
- TypeMask get constListType;
- TypeMask get fixedListType;
- TypeMask get growableListType;
- TypeMask get mapType;
- TypeMask get constMapType;
- TypeMask get stringType;
- TypeMask get typeType;
}
/**
@@ -65,6 +50,22 @@
}
}
+ TypeMask dynamicType;
+ TypeMask nullType;
+ TypeMask intType;
+ TypeMask doubleType;
+ TypeMask numType;
+ TypeMask boolType;
+ TypeMask functionType;
+ TypeMask listType;
+ TypeMask constListType;
+ TypeMask fixedListType;
+ TypeMask growableListType;
+ TypeMask mapType;
+ TypeMask constMapType;
+ TypeMask stringType;
+ TypeMask typeType;
+
/// Replaces native types by their backend implementation.
Element normalize(Element cls) {
if (cls == compiler.boolClass) {
@@ -130,8 +131,12 @@
TypeMask _best(var type1, var type2) {
if (type1 == null) return type2;
if (type2 == null) return type1;
+ if (type1.isContainer) type1 = type1.asFlat;
+ if (type2.isContainer) type2 = type2.asFlat;
if (type1.isExact) {
if (type2.isExact) {
+ // TODO(polux): Update the code to not have this situation.
+ if (type1.base != type2.base) return type1;
assert(same(type1.base, type2.base));
return type1.isNullable ? type2 : type1;
} else {
@@ -174,11 +179,56 @@
}
}
+ // TODO(ngeoffray): Get rid of this method. Unit tests don't always
+ // ensure these classes are resolved.
+ rawTypeOf(ClassElement cls) {
+ cls.ensureResolved(compiler);
+ assert(cls.rawType != null);
+ return cls.rawType;
+ }
+
+ void initializeTypes() {
+ nullType = new TypeMask.empty();
+
+ Backend backend = compiler.backend;
+ intType = new TypeMask.nonNullExact(
+ rawTypeOf(backend.intImplementation));
+ doubleType = new TypeMask.nonNullExact(
+ rawTypeOf(backend.doubleImplementation));
+ numType = new TypeMask.nonNullSubclass(
+ rawTypeOf(backend.numImplementation));
+ stringType = new TypeMask.nonNullExact(
+ rawTypeOf(backend.stringImplementation));
+ boolType = new TypeMask.nonNullExact(
+ rawTypeOf(backend.boolImplementation));
+
+ listType = new TypeMask.nonNullExact(
+ rawTypeOf(backend.listImplementation));
+ constListType = new TypeMask.nonNullExact(
+ rawTypeOf(backend.constListImplementation));
+ fixedListType = new TypeMask.nonNullExact(
+ rawTypeOf(backend.fixedListImplementation));
+ growableListType = new TypeMask.nonNullExact(
+ rawTypeOf(backend.growableListImplementation));
+
+ mapType = new TypeMask.nonNullSubtype(
+ rawTypeOf(backend.mapImplementation));
+ constMapType = new TypeMask.nonNullSubtype(
+ rawTypeOf(backend.constMapImplementation));
+ functionType = new TypeMask.nonNullSubtype(
+ rawTypeOf(backend.functionImplementation));
+ typeType = new TypeMask.nonNullExact(
+ rawTypeOf(backend.typeImplementation));
+
+ dynamicType = new TypeMask.subclass(rawTypeOf(compiler.objectClass));
+ }
+
/**
* Called when resolution is complete.
*/
void onResolutionComplete(Element mainElement) {
measure(() {
+ initializeTypes();
typesInferrer.analyzeMain(mainElement);
if (concreteTypesInferrer != null) {
bool success = concreteTypesInferrer.analyzeMain(mainElement);
@@ -190,6 +240,8 @@
}
}
});
+ compiler.containerTracer.analyze();
+ typesInferrer.clear();
}
/**
diff --git a/sdk/lib/_internal/compiler/implementation/types/union_type_mask.dart b/sdk/lib/_internal/compiler/implementation/types/union_type_mask.dart
index 0eb33f6..357486b 100644
--- a/sdk/lib/_internal/compiler/implementation/types/union_type_mask.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/union_type_mask.dart
@@ -30,7 +30,13 @@
} else if (mask.isEmpty && !mask.isNullable) {
continue;
} else {
- FlatTypeMask flatMask = mask;
+ FlatTypeMask flatMask;
+ if (mask.isContainer) {
+ ContainerTypeMask container = mask;
+ flatMask = container.asFlat;
+ } else {
+ flatMask = mask;
+ }
assert(flatMask.base == null
|| flatMask.base.element != compiler.dynamicClass);
int inListIndex = -1;
@@ -140,6 +146,7 @@
}
TypeMask union(var other, Compiler compiler) {
+ if (other.isContainer) other = other.asFlat;
if (!other.isUnion && disjointMasks.contains(other)) return this;
List<FlatTypeMask> newList =
@@ -154,6 +161,7 @@
}
TypeMask intersection(var other, Compiler compiler) {
+ if (other.isContainer) other = other.asFlat;
if (!other.isUnion && disjointMasks.contains(other)) return other;
List<TypeMask> intersections = <TypeMask>[];
@@ -189,6 +197,7 @@
bool get isNullable => disjointMasks.any((e) => e.isNullable);
bool get isExact => false;
bool get isUnion => true;
+ bool get isContainer => false;
bool containsOnlyInt(Compiler compiler) => false;
bool containsOnlyDouble(Compiler compiler) => false;
diff --git a/sdk/lib/_internal/lib/async_patch.dart b/sdk/lib/_internal/lib/async_patch.dart
index 9d8b237e..78ce9cd 100644
--- a/sdk/lib/_internal/lib/async_patch.dart
+++ b/sdk/lib/_internal/lib/async_patch.dart
@@ -7,24 +7,23 @@
import 'dart:_isolate_helper' show IsolateNatives, TimerImpl;
import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS;
-patch class Timer {
- patch factory Timer(Duration duration, void callback()) {
- int milliseconds = duration.inMilliseconds;
- if (milliseconds < 0) milliseconds = 0;
- return new TimerImpl(milliseconds, callback);
- }
+patch Timer _createTimer(Duration duration, void callback()) {
+ int milliseconds = duration.inMilliseconds;
+ if (milliseconds < 0) milliseconds = 0;
+ return new TimerImpl(milliseconds, callback);
+}
- patch factory Timer.periodic(Duration duration, void callback(Timer timer)) {
- int milliseconds = duration.inMilliseconds;
- if (milliseconds < 0) milliseconds = 0;
- return new TimerImpl.periodic(milliseconds, callback);
- }
+patch Timer _createPeriodicTimer(Duration duration,
+ void callback(Timer timer)) {
+ int milliseconds = duration.inMilliseconds;
+ if (milliseconds < 0) milliseconds = 0;
+ return new TimerImpl.periodic(milliseconds, callback);
}
patch class _AsyncRun {
patch static void _enqueueImmediate(void callback()) {
// TODO(9002): don't use the Timer to enqueue the immediate callback.
- Timer.run(callback);
+ _createTimer(Duration.ZERO, callback);
}
}
diff --git a/sdk/lib/_internal/lib/js_mirrors.dart b/sdk/lib/_internal/lib/js_mirrors.dart
index 18d6e6d..59b8355 100644
--- a/sdk/lib/_internal/lib/js_mirrors.dart
+++ b/sdk/lib/_internal/lib/js_mirrors.dart
@@ -93,23 +93,49 @@
}
}
-class JsTypeMirror implements TypeMirror {
- final Symbol simpleName;
- JsTypeMirror(this.simpleName);
+abstract class JsMirror {
+ const JsMirror();
+
+ abstract String get _prettyName;
+
+ String toString() => _prettyName;
}
-class JsLibraryMirror extends JsObjectMirror implements LibraryMirror {
+abstract class JsDeclarationMirror extends JsMirror
+ implements DeclarationMirror {
final Symbol simpleName;
+
+ const JsDeclarationMirror(this.simpleName);
+
+ bool get isPrivate => n(simpleName).startsWith('_');
+
+ bool get isTopLevel => owner != null && owner is LibraryMirror;
+
+ String toString() => "$_prettyName on '${n(simpleName)}'";
+}
+
+class JsTypeMirror extends JsDeclarationMirror implements TypeMirror {
+ JsTypeMirror(Symbol simpleName)
+ : super(simpleName);
+
+ String get _prettyName => 'TypeMirror';
+}
+
+class JsLibraryMirror extends JsDeclarationMirror with JsObjectMirror
+ implements LibraryMirror {
final Uri uri;
final List<String> _classes;
final List<String> _functions;
final List _metadata;
- JsLibraryMirror(this.simpleName,
+ JsLibraryMirror(Symbol simpleName,
this.uri,
this._classes,
this._functions,
- this._metadata);
+ this._metadata)
+ : super(simpleName);
+
+ String get _prettyName => 'LibraryMirror';
Symbol get qualifiedName => simpleName;
@@ -321,11 +347,11 @@
return JSInvocationMirror.invokeFromMirror(invocation, reflectee);
}
- String toString() => 'InstanceMirror($reflectee)';
+ String toString() => 'InstanceMirror on ${Error.safeToString(reflectee)}';
}
-class JsClassMirror extends JsObjectMirror implements ClassMirror {
- final Symbol simpleName;
+class JsClassMirror extends JsTypeMirror with JsObjectMirror
+ implements ClassMirror {
final _jsConstructor;
final String _fields;
final List _fieldsMetadata;
@@ -336,10 +362,13 @@
// Set as side-effect of accessing JsLibraryMirror.classes.
JsLibraryMirror _owner;
- JsClassMirror(this.simpleName,
+ JsClassMirror(Symbol simpleName,
this._jsConstructor,
this._fields,
- this._fieldsMetadata);
+ this._fieldsMetadata)
+ : super(simpleName);
+
+ String get _prettyName => 'ClassMirror';
Symbol get qualifiedName => computeQualifiedName(owner, simpleName);
@@ -516,13 +545,10 @@
}
return _superclass == this ? null : _superclass;
}
-
- String toString() => 'ClassMirror(${n(simpleName)})';
}
-class JsVariableMirror implements VariableMirror {
+class JsVariableMirror extends JsDeclarationMirror implements VariableMirror {
// TODO(ahe): The values in these fields are virtually untested.
- final Symbol simpleName;
final String _jsName;
final bool isFinal;
final bool isStatic;
@@ -530,11 +556,12 @@
DeclarationMirror _owner;
List _metadata;
- JsVariableMirror(this.simpleName,
+ JsVariableMirror(Symbol simpleName,
this._jsName,
this.isFinal,
this.isStatic,
- this._metadataFunction);
+ this._metadataFunction)
+ : super(simpleName);
factory JsVariableMirror.from(String descriptor, metadataFunction) {
int length = descriptor.length;
@@ -556,6 +583,8 @@
s(accessorName), jsName, isFinal, false, metadataFunction);
}
+ String get _prettyName => 'VariableMirror';
+
TypeMirror get type => JsMirrorSystem._dynamicType;
DeclarationMirror get owner => _owner;
@@ -570,12 +599,6 @@
return _metadata.map(reflect).toList();
}
- bool get isPrivate => n(simpleName).startsWith('_');
-
- bool get isTopLevel => owner != null && owner is LibraryMirror;
-
- String toString() => 'VariableMirror(${n(qualifiedName)})';
-
static int fieldCode(int code) {
if (code >= 60 && code <= 64) return code - 59;
if (code >= 123 && code <= 126) return code - 117;
@@ -585,7 +608,8 @@
}
class JsClosureMirror extends JsInstanceMirror implements ClosureMirror {
- JsClosureMirror(reflectee) : super(reflectee);
+ JsClosureMirror(reflectee)
+ : super(reflectee);
MethodMirror get function {
disableTreeShaking();
@@ -628,10 +652,11 @@
return new Future<InstanceMirror>(
() => apply(positionalArguments, namedArguments));
}
+
+ String toString() => "ClosureMirror on '${Error.safeToString(reflectee)}'";
}
-class JsMethodMirror implements MethodMirror {
- final Symbol simpleName;
+class JsMethodMirror extends JsDeclarationMirror implements MethodMirror {
final _jsFunction;
final int _parameterCount;
final bool isGetter;
@@ -640,12 +665,13 @@
DeclarationMirror _owner;
List _metadata;
- JsMethodMirror(this.simpleName,
+ JsMethodMirror(Symbol simpleName,
this._jsFunction,
this._parameterCount,
this.isGetter,
this.isSetter,
- this.isStatic);
+ this.isStatic)
+ : super(simpleName);
factory JsMethodMirror.fromUnmangledName(String name, jsFunction) {
List<String> info = name.split(':');
@@ -670,6 +696,8 @@
isGetter, isSetter, false);
}
+ String get _prettyName => 'MethodMirror';
+
List<ParameterMirror> get parameters {
// TODO(ahe): Fill the list with parameter mirrors.
return new List<ParameterMirror>(_parameterCount);
@@ -685,18 +713,6 @@
}
return _metadata.map(reflect).toList();
}
-
- // TODO(ahe): Share with VariableMirror.
- bool get isPrivate => n(simpleName).startsWith('_');
-
- // TODO(ahe): Share with VariableMirror.
- bool get isTopLevel => owner != null && owner is LibraryMirror;
-
- String toString() {
- return
- 'MethodMirror(${n(simpleName)}'
- '${isSetter ? ", setter" : (isGetter ? ", getter" : "")})';
- }
}
Symbol computeQualifiedName(DeclarationMirror owner, Symbol simpleName) {
diff --git a/sdk/lib/_internal/lib/regexp_helper.dart b/sdk/lib/_internal/lib/regexp_helper.dart
index 0e923a3..c9d9636 100644
--- a/sdk/lib/_internal/lib/regexp_helper.dart
+++ b/sdk/lib/_internal/lib/regexp_helper.dart
@@ -7,37 +7,56 @@
// Helper method used by internal libraries.
regExpGetNative(JSSyntaxRegExp regexp) => regexp._nativeRegExp;
-class JSSyntaxRegExp implements RegExp {
- final String _pattern;
- final bool _isMultiLine;
- final bool _isCaseSensitive;
- var _nativeRegExp;
+/**
+ * Returns a native version of the RegExp with the global flag set.
+ *
+ * The RegExp's `lastIndex` property is zero when it is returned.
+ *
+ * The returned regexp is shared, and its `lastIndex` property may be
+ * modified by other uses, so the returned regexp must be used immediately
+ * when it's returned, with no user-provided code run in between.
+ */
+regExpGetGlobalNative(JSSyntaxRegExp regexp) {
+ var regexp = regexp._nativeGlobalVersion;
+ JS("void", "#.lastIndex = 0", regexp);
+ return regexp;
+}
- JSSyntaxRegExp._internal(String pattern,
- bool multiLine,
- bool caseSensitive,
- bool global)
- : _nativeRegExp = makeNative(pattern, multiLine, caseSensitive, global),
- this._pattern = pattern,
- this._isMultiLine = multiLine,
- this._isCaseSensitive = caseSensitive;
+class JSSyntaxRegExp implements RegExp {
+ final _nativeRegExp;
+ var _nativeGlobalRegExp;
+ var _nativeAnchoredRegExp;
JSSyntaxRegExp(String pattern,
- {bool multiLine: false,
- bool caseSensitive: true})
- : this._internal(pattern, multiLine, caseSensitive, false);
+ { bool multiLine: false,
+ bool caseSensitive: true })
+ : this._nativeRegExp =
+ makeNative(pattern, multiLine, caseSensitive, false);
- JSSyntaxRegExp._globalVersionOf(JSSyntaxRegExp other)
- : this._internal(other.pattern,
- other.isMultiLine,
- other.isCaseSensitive,
- true);
+ get _nativeGlobalVersion {
+ if (_nativeGlobalRegExp != null) return _nativeGlobalRegExp;
+ return _nativeGlobalRegExp = makeNative(_pattern,
+ _isMultiLine,
+ _isCaseSensitive,
+ true);
+ }
- JSSyntaxRegExp._anchoredVersionOf(JSSyntaxRegExp other)
- : this._internal(other.pattern + "|()",
- other.isMultiLine,
- other.isCaseSensitive,
- true);
+ get _nativeAnchoredVersion {
+ if (_nativeAnchoredRegExp != null) return _nativeAnchoredRegExp;
+ // An "anchored version" of a regexp is created by adding "|()" to the
+ // source. This means that the regexp always matches at the first position
+ // that it tries, and you can see if the original regexp matched, or it
+ // was the added zero-width match that matched, by looking at the last
+ // capture. If it is a String, the match participated, otherwise it didn't.
+ return _nativeAnchoredRegExp = makeNative("$_pattern|()",
+ _isMultiLine,
+ _isCaseSensitive,
+ true);
+ }
+
+ String get _pattern => JS("String", "#.source", _nativeRegExp);
+ bool get _isMultiLine => JS("bool", "#.multiline", _nativeRegExp);
+ bool get _isCaseSensitive => JS("bool", "!#.ignoreCase", _nativeRegExp);
static makeNative(
String pattern, bool multiLine, bool caseSensitive, bool global) {
@@ -68,10 +87,7 @@
List<String> m =
JS('=List|Null', r'#.exec(#)', _nativeRegExp, checkString(str));
if (m == null) return null;
- var matchStart = JS('int', r'#.index', m);
- // m.lastIndex only works with flag 'g'.
- var matchEnd = matchStart + m[0].length;
- return new _MatchImplementation(pattern, str, matchStart, matchEnd, m);
+ return new _MatchImplementation(this, m);
}
bool hasMatch(String str) {
@@ -80,7 +96,8 @@
String stringMatch(String str) {
var match = firstMatch(str);
- return match == null ? null : match.group(0);
+ if (match != null) return match.group(0);
+ return null;
}
Iterable<Match> allMatches(String str) {
@@ -88,24 +105,31 @@
return new _AllMatchesIterable(this, str);
}
+ Match _execGlobal(String string, int start) {
+ Object regexp = _nativeGlobalVersion;
+ JS("void", "#.lastIndex = #", regexp, start);
+ List match = JS("=List|Null", "#.exec(#)", regexp, string);
+ if (match == null) return null;
+ return new _MatchImplementation(this, match);
+ }
+
+ Match _execAnchored(String string, int start) {
+ Object regexp = _nativeAnchoredVersion;
+ JS("void", "#.lastIndex = #", regexp, start);
+ List match = JS("=List|Null", "#.exec(#)", regexp, string);
+ if (match == null) return null;
+ // If the last capture group participated, the original regexp did not
+ // match at the start position.
+ if (match[match.length - 1] != null) return null;
+ match.length -= 1;
+ return new _MatchImplementation(this, match);
+ }
+
Match matchAsPrefix(String string, [int start = 0]) {
if (start < 0 || start > string.length) {
throw new RangeError.range(start, 0, string.length);
}
- // An "anchored version" of a regexp is created by adding "|()" to the
- // source. This means that the regexp always matches at the first position
- // that it tries, and you can see if the original regexp matched, or it
- // was the added zero-width match that matched, by looking at the last
- // capture. If it is a String, the match participated, otherwise it didn't.
- JSSyntaxRegExp regexp = new JSSyntaxRegExp._anchoredVersionOf(this);
- if (start > 0) {
- JS("void", "#.lastIndex = #", regExpGetNative(regexp), start);
- }
- _MatchImplementation match = regexp.firstMatch(string);
- if (match == null) return null;
- if (match._groups[match._groups.length - 1] != null) return null;
- match._groups.length -= 1;
- return match;
+ return _execAnchored(string, start);
}
String get pattern => _pattern;
@@ -115,21 +139,22 @@
class _MatchImplementation implements Match {
final Pattern pattern;
- final String str;
- final int start;
- final int end;
- final List<String> _groups;
+ // Contains a JS RegExp match object.
+ // It is an Array of String values with extra "index" and "input" properties.
+ final List<String> _match;
- const _MatchImplementation(
- this.pattern,
- this.str,
- int this.start,
- int this.end,
- List<String> this._groups);
+ _MatchImplementation(this.pattern, this._match) {
+ assert(JS("var", "#.input", _match) is String);
+ assert(JS("var", "#.index", _match) is int);
+ }
- String group(int index) => _groups[index];
+ String get str => JS("String", "#.input", _match);
+ int get start => JS("int", "#.index", _match);
+ int get end => start + _match[0].length;
+
+ String group(int index) => _match[index];
String operator [](int index) => group(index);
- int get groupCount => _groups.length - 1;
+ int get groupCount => _match.length - 1;
List<String> groups(List<int> groups) {
List<String> out = [];
@@ -142,54 +167,41 @@
class _AllMatchesIterable extends IterableBase<Match> {
final JSSyntaxRegExp _re;
- final String _str;
+ final String _string;
- const _AllMatchesIterable(this._re, this._str);
+ const _AllMatchesIterable(this._re, this._string);
- Iterator<Match> get iterator => new _AllMatchesIterator(_re, _str);
+ Iterator<Match> get iterator => new _AllMatchesIterator(_re, _string);
}
class _AllMatchesIterator implements Iterator<Match> {
- final RegExp _regExp;
- final RegExp _globalRegExp;
- String _str;
+ final JSSyntaxRegExp _regExp;
+ String _string;
Match _current;
- _AllMatchesIterator(JSSyntaxRegExp re, String this._str)
- : _regExp = re,
- _globalRegExp = new JSSyntaxRegExp._globalVersionOf(re);
+ _AllMatchesIterator(this._regExp, this._string);
Match get current => _current;
bool moveNext() {
- if (_str == null) return false;
- // firstMatch actually acts as nextMatch because of
- // hidden global flag.
- if (_current != null && _current.start == _current.end) {
- // Advance implicit start-position if last match was empty.
- JS("void", "#.lastIndex++", regExpGetNative(_globalRegExp));
+ if (_string == null) return false;
+ int index = 0;
+ if (_current != null) {
+ index = _current.end;
+ if (_current.start == index) {
+ index++;
+ }
}
- List<String> m =
- JS('=List|Null', r'#.exec(#)', regExpGetNative(_globalRegExp), _str);
- if (m == null) {
- _current = null;
- _str = null; // Marks iteration as ended.
+ _current = _regExp._execGlobal(_string, index);
+ if (_current == null) {
+ _string = null; // Marks iteration as ended.
return false;
}
- var matchStart = JS('int', r'#.index', m);
- var matchEnd = matchStart + m[0].length;
- _current = new _MatchImplementation(_regExp, _str, matchStart, matchEnd, m);
return true;
}
}
-Match firstMatchAfter(JSSyntaxRegExp re, String str, int start) {
- JSSyntaxRegExp global = new JSSyntaxRegExp._globalVersionOf(re);
- JS("void", "#.lastIndex = #", regExpGetNative(global), start);
- List<String> m =
- JS('=List|Null', r'#.exec(#)', regExpGetNative(global), checkString(str));
- if (m == null) return null;
- var matchStart = JS('int', r'#.index', m);
- var matchEnd = matchStart + m[0].length;
- return new _MatchImplementation(re, str, matchStart, matchEnd, m);
+/** Find the first match of [regExp] in [string] at or after [start]. */
+Match firstMatchAfter(JSSyntaxRegExp regExp, String string, int start) {
+ return regExp._execGlobal(string, start);
}
diff --git a/sdk/lib/_internal/lib/string_helper.dart b/sdk/lib/_internal/lib/string_helper.dart
index f321fbb..1c430c8 100644
--- a/sdk/lib/_internal/lib/string_helper.dart
+++ b/sdk/lib/_internal/lib/string_helper.dart
@@ -94,14 +94,14 @@
return result.toString();
}
} else {
- var quoter = JS('', "new RegExp(#, 'g')", r'[-[\]{}()*+?.,\\^$|#\s]');
+ var quoter = JS('', "new RegExp(#, 'g')", r'[[\]{}()*+?.\\^$|]');
var quoted = JS('String', r'#.replace(#, "\\$&")', from, quoter);
var replacer = JS('', "new RegExp(#, 'g')", quoted);
return stringReplaceJS(receiver, replacer, to);
}
} else if (from is JSSyntaxRegExp) {
- var re = new JSSyntaxRegExp._globalVersionOf(from);
- return stringReplaceJS(receiver, re._nativeRegExp, to);
+ var re = regExpGetGlobalNative(from);
+ return stringReplaceJS(receiver, re, to);
} else {
checkNull(from);
// TODO(floitsch): implement generic String.replace (with patterns).
diff --git a/sdk/lib/async/async.dart b/sdk/lib/async/async.dart
index b028d80..acd808a 100644
--- a/sdk/lib/async/async.dart
+++ b/sdk/lib/async/async.dart
@@ -16,3 +16,4 @@
part 'stream_impl.dart';
part 'stream_pipe.dart';
part 'timer.dart';
+part 'zone.dart';
diff --git a/sdk/lib/async/async_sources.gypi b/sdk/lib/async/async_sources.gypi
index b39435a..dc0dbec 100644
--- a/sdk/lib/async/async_sources.gypi
+++ b/sdk/lib/async/async_sources.gypi
@@ -17,5 +17,6 @@
'stream_impl.dart',
'stream_pipe.dart',
'timer.dart',
+ 'zone.dart',
],
}
diff --git a/sdk/lib/async/event_loop.dart b/sdk/lib/async/event_loop.dart
index 2802e40..bc0a638 100644
--- a/sdk/lib/async/event_loop.dart
+++ b/sdk/lib/async/event_loop.dart
@@ -18,13 +18,23 @@
callback();
} catch (e) {
_AsyncRun._enqueueImmediate(_asyncRunCallback);
- throw;
+ rethrow;
}
}
// Any new callback must register a callback function now.
_callbacksAreEnqueued = false;
}
+void _scheduleAsyncCallback(callback) {
+ // Optimizing a group of Timer.run callbacks to be executed in the
+ // same Timer callback.
+ _asyncCallbacks.add(callback);
+ if (!_callbacksAreEnqueued) {
+ _AsyncRun._enqueueImmediate(_asyncRunCallback);
+ _callbacksAreEnqueued = true;
+ }
+}
+
/**
* Runs the given [callback] asynchronously.
*
@@ -45,13 +55,7 @@
* }
*/
void runAsync(void callback()) {
- // Optimizing a group of Timer.run callbacks to be executed in the
- // same Timer callback.
- _asyncCallbacks.add(callback);
- if (!_callbacksAreEnqueued) {
- _AsyncRun._enqueueImmediate(_asyncRunCallback);
- _callbacksAreEnqueued = true;
- }
+ _Zone._current.runAsync(callback);
}
class _AsyncRun {
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index 47c51ec..be0b7a2 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -11,7 +11,10 @@
final Future<T> future;
bool _isComplete = false;
- _Completer() : future = new _FutureImpl<T>();
+ _Completer() : future = new _FutureImpl<T>() {
+ _FutureImpl futureImpl = future;
+ futureImpl._zone.expectCallback();
+ }
void _setFutureValue(T value);
void _setFutureError(error);
@@ -19,6 +22,8 @@
void complete([T value]) {
if (_isComplete) throw new StateError("Future already completed");
_isComplete = true;
+ _FutureImpl futureImpl = future;
+ futureImpl._zone.cancelCallbackExpectation();
_setFutureValue(value);
}
@@ -29,7 +34,13 @@
// Force the stack trace onto the error, even if it already had one.
_attachStackTrace(error, stackTrace);
}
- _setFutureError(error);
+ _FutureImpl futureImpl = future;
+ if (futureImpl._inSameErrorZone(_Zone.current)) {
+ futureImpl._zone.cancelCallbackExpectation();
+ _setFutureError(error);
+ } else {
+ _Zone.current.handleUncaughtError(error);
+ }
}
bool get isCompleted => _isComplete;
@@ -74,6 +85,8 @@
}
void _sendValue(T value);
void _sendError(error);
+
+ bool _inSameErrorZone(_Zone otherZone);
}
/** Adapter for a [_FutureImpl] to be a future result listener. */
@@ -83,6 +96,61 @@
_FutureListenerWrapper(this.future);
_sendValue(T value) { future._setValue(value); }
_sendError(error) { future._setError(error); }
+ bool _inSameErrorZone(_Zone otherZone) => future._inSameErrorZone(otherZone);
+}
+
+/**
+ * This listener is installed at error-zone boundaries. It signals an
+ * uncaught error in the zone of origin when an error is sent from one error
+ * zone to another.
+ *
+ * When a Future is listening to another Future and they have not been
+ * instantiated in the same error-zone then Futures put an instance of this
+ * class between them (see [_FutureImpl._addListener]).
+ *
+ * For example:
+ *
+ * var completer = new Completer();
+ * var future = completer.future.then((x) => x);
+ * catchErrors(() {
+ * var future2 = future.catchError(print);
+ * });
+ * completer.completeError(499);
+ *
+ * In this example `future` and `future2` are in different error-zones. The
+ * error (499) that originates outside `catchErrors` must not reach the
+ * `catchError` future (`future2`) inside `catchErrors`.
+ *
+ * When invoking `catchError` on `future` the Future installs an
+ * [_ErrorZoneBoundaryListener] between itself and the result, `future2`.
+ *
+ * Conceptually _ErrorZoneBoundaryListeners could be implemented as
+ * `catchError`s on the origin future as well.
+ */
+class _ErrorZoneBoundaryListener implements _FutureListener {
+ _FutureListener _nextListener;
+ final _FutureListener _listener;
+
+ _ErrorZoneBoundaryListener(this._listener);
+
+ bool _inSameErrorZone(_Zone otherZone) {
+ // Should never be called. We use [_inSameErrorZone] to know if we have
+ // to insert an instance of [_ErrorZoneBoundaryListener] (and in the
+ // controller). Once we have inserted one we should never need to use it
+ // anymore.
+ throw new UnsupportedError(
+ "A Zone boundary doesn't support the inSameErrorZone test.");
+ }
+
+ void _sendValue(value) {
+ _listener._sendValue(value);
+ }
+
+ void _sendError(error) {
+ // We are not allowed to send an error from one error-zone to another.
+ // This is the whole purpose of this class.
+ _Zone.current.handleUncaughtError(error);
+ }
}
class _FutureImpl<T> implements Future<T> {
@@ -118,6 +186,8 @@
/** Whether the future is complete, and as what. */
int _state = _INCOMPLETE;
+ final _Zone _zone = _Zone.current.fork();
+
bool get _isChained => (_state & _CHAINED) != 0;
bool get _hasChainedListener => _state == _CHAINED;
bool get _isComplete => _state >= _VALUE;
@@ -215,6 +285,10 @@
Stream<T> asStream() => new Stream.fromFuture(this);
+ bool _inSameErrorZone(_Zone otherZone) {
+ return _zone.inSameErrorZone(otherZone);
+ }
+
void _setValue(T value) {
if (_isComplete) throw new StateError("Future already completed");
_FutureListener listeners = _isChained ? null : _removeListeners();
@@ -267,17 +341,16 @@
_clearUnhandledError();
// TODO(floitsch): Hook this into unhandled error handling.
var error = _resultOrListeners;
- print("Uncaught Error: ${error}");
- var trace = getAttachedStackTrace(error);
- if (trace != null) {
- print("Stack Trace:\n$trace\n");
- }
- throw error;
+ _zone.handleUncaughtError(error);
}
});
}
void _addListener(_FutureListener listener) {
+ assert(listener._nextListener == null);
+ if (!listener._inSameErrorZone(_zone)) {
+ listener = new _ErrorZoneBoundaryListener(listener);
+ }
if (_isChained) {
_state = _CHAINED; // In case it was _CHAINED_UNLISTENED.
_FutureImpl resultSource = _chainSource;
@@ -298,7 +371,6 @@
});
} else {
assert(!_isComplete);
- assert(listener._nextListener == null);
listener._nextListener = _resultOrListeners;
_resultOrListeners = listener;
}
@@ -434,13 +506,24 @@
// _FutureListener implementation.
_FutureListener _nextListener;
- void _sendValue(S value);
+ _TransformFuture() {
+ _zone.expectCallback();
+ }
- void _sendError(error);
+ void _sendValue(S value) {
+ _zone.executeCallback(() => _zonedSendValue(value));
+ }
+
+ void _sendError(error) {
+ _zone.executeCallback(() => _zonedSendError(error));
+ }
void _subscribeTo(_FutureImpl future) {
future._addListener(this);
}
+
+ void _zonedSendValue(S value);
+ void _zonedSendError(error);
}
/** The onValue and onError handlers return either a value or a future */
@@ -459,7 +542,7 @@
_ThenFuture(this._onValue);
- _sendValue(S value) {
+ _zonedSendValue(S value) {
assert(_onValue != null);
var result;
try {
@@ -471,7 +554,7 @@
_setOrChainValue(result);
}
- void _sendError(error) {
+ void _zonedSendError(error) {
_setError(error);
}
}
@@ -483,11 +566,11 @@
_CatchErrorFuture(this._onError, this._test);
- _sendValue(T value) {
+ _zonedSendValue(T value) {
_setValue(value);
}
- _sendError(error) {
+ _zonedSendError(error) {
assert(_onError != null);
// if _test is supplied, check if it returns true, otherwise just
// forward the error unmodified.
@@ -524,7 +607,7 @@
// The _sendValue method is inherited from ThenFuture.
- void _sendError(error) {
+ void _zonedSendError(error) {
assert(_onError != null);
var result;
try {
@@ -543,7 +626,7 @@
_WhenFuture(this._action);
- void _sendValue(T value) {
+ void _zonedSendValue(T value) {
try {
var result = _action();
if (result is Future) {
@@ -560,7 +643,7 @@
_setValue(value);
}
- void _sendError(error) {
+ void _zonedSendError(error) {
try {
var result = _action();
if (result is Future) {
diff --git a/sdk/lib/async/stream_controller.dart b/sdk/lib/async/stream_controller.dart
index abfd648..2cdbeaf 100644
--- a/sdk/lib/async/stream_controller.dart
+++ b/sdk/lib/async/stream_controller.dart
@@ -357,7 +357,7 @@
try {
notificationHandler();
} catch (e, s) {
- _throwDelayed(e, s);
+ _Zone.current.handleUncaughtError(_asyncError(e, s));
}
}
diff --git a/sdk/lib/async/stream_impl.dart b/sdk/lib/async/stream_impl.dart
index e46ba50..85aa083 100644
--- a/sdk/lib/async/stream_impl.dart
+++ b/sdk/lib/async/stream_impl.dart
@@ -4,20 +4,6 @@
part of dart.async;
-/** Throws the given error in the next cycle. */
-_throwDelayed(var error, [Object stackTrace]) {
- // We are going to reach the top-level here, but there might be a global
- // exception handler. This means that we shouldn't print the stack trace.
- // TODO(floitsch): Find better solution that doesn't print the stack trace
- // if there is a global exception handler.
- runAsync(() {
- if (stackTrace != null) print(stackTrace);
- var trace = getAttachedStackTrace(error);
- if (trace != null && trace != stackTrace) print(trace);
- throw error;
- });
-}
-
/** Abstract and private interface for a place to put events. */
abstract class _EventSink<T> {
void _add(T data);
@@ -96,6 +82,7 @@
Function _onData;
_ErrorHandler _onError;
_DoneHandler _onDone;
+ final _Zone _zone = _Zone.current;
/** Bit vector based on state-constants above. */
int _state;
@@ -115,6 +102,7 @@
assert(_onData != null);
assert(_onError != null);
assert(_onDone != null);
+ _zone.expectCallback();
}
/**
@@ -233,6 +221,7 @@
void _cancel() {
_state |= _STATE_CANCELED;
+ _zone.cancelCallbackExpectation();
if (_hasPending) {
_pending.cancelSchedule();
}
@@ -293,7 +282,8 @@
// Hooks called when the input is paused, unpaused or canceled.
// These must not throw. If overwritten to call user code, include suitable
- // try/catch wrapping and send any errors to [_throwDelayed].
+ // try/catch wrapping and send any errors to
+ // [_Zone.current.handleUncaughtError].
void _onPause() {
assert(_isInputPaused);
}
@@ -334,11 +324,7 @@
assert(!_inCallback);
bool wasInputPaused = _isInputPaused;
_state |= _STATE_IN_CALLBACK;
- try {
- _onData(data);
- } catch (e, s) {
- _throwDelayed(e, s);
- }
+ _zone.executePeriodicCallbackGuarded(() => _onData(data));
_state &= ~_STATE_IN_CALLBACK;
_checkState(wasInputPaused);
}
@@ -349,10 +335,11 @@
assert(!_inCallback);
bool wasInputPaused = _isInputPaused;
_state |= _STATE_IN_CALLBACK;
- try {
- _onError(error);
- } catch (e, s) {
- _throwDelayed(e, s);
+ if (!_zone.inSameErrorZone(_Zone.current)) {
+ // Errors are not allowed to traverse zone boundaries.
+ _Zone.current.handleUncaughtError(error);
+ } else {
+ _zone.executePeriodicCallbackGuarded(() => _onError(error));
}
_state &= ~_STATE_IN_CALLBACK;
if (_cancelOnError) {
@@ -366,11 +353,7 @@
assert(!_isPaused);
assert(!_inCallback);
_state |= (_STATE_CANCELED | _STATE_CLOSED | _STATE_IN_CALLBACK);
- try {
- _onDone();
- } catch (e, s) {
- _throwDelayed(e, s);
- }
+ _zone.executeCallbackGuarded(_onDone);
_onCancel(); // No checkState after cancel, it is always the last event.
_state &= ~_STATE_IN_CALLBACK;
}
@@ -551,7 +534,7 @@
/** Default error handler, reports the error to the global handler. */
void _nullErrorHandler(error) {
- _throwDelayed(error);
+ _Zone.current.handleUncaughtError(error);
}
/** Default done handler, does nothing. */
diff --git a/sdk/lib/async/timer.dart b/sdk/lib/async/timer.dart
index bfa7ced..3544728 100644
--- a/sdk/lib/async/timer.dart
+++ b/sdk/lib/async/timer.dart
@@ -29,7 +29,9 @@
* Note: If Dart code using Timer is compiled to JavaScript, the finest
* granularity available in the browser is 4 milliseconds.
*/
- external factory Timer(Duration duration, void callback());
+ factory Timer(Duration duration, void callback()) {
+ return _Zone.current.createTimer(duration, callback);
+ }
/**
* Creates a new repeating timer.
@@ -37,8 +39,10 @@
* The [callback] is invoked repeatedly with [duration] intervals until
* canceled. A negative duration is treated similar to a duration of 0.
*/
- external factory Timer.periodic(Duration duration,
- void callback(Timer timer));
+ factory Timer.periodic(Duration duration,
+ void callback(Timer timer)) {
+ return _Zone.current.createPeriodicTimer(duration, callback);
+ }
/**
* Runs the given [callback] asynchronously as soon as possible.
@@ -54,3 +58,7 @@
*/
void cancel();
}
+
+external Timer _createTimer(Duration duration, void callback());
+external Timer _createPeriodicTimer(Duration duration,
+ void callback(Timer timer));
diff --git a/sdk/lib/async/zone.dart b/sdk/lib/async/zone.dart
new file mode 100644
index 0000000..b92aa16
--- /dev/null
+++ b/sdk/lib/async/zone.dart
@@ -0,0 +1,504 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.async;
+
+/**
+ * A Zone represents the asynchronous version of a dynamic extent. Asynchronous
+ * callbacks are executed in the zone they have been queued in. For example,
+ * the callback of a `future.then` is executed in the same zone as the one where
+ * the `then` was invoked.
+ */
+abstract class _Zone {
+ /// The currently running zone.
+ static _Zone _current = new _DefaultZone();
+
+ static _Zone get current => _current;
+
+ void handleUncaughtError(error);
+
+ /**
+ * Returns true if `this` and [otherZone] are in the same error zone.
+ */
+ bool inSameErrorZone(_Zone otherZone);
+
+ /**
+ * Returns a zone for reentry in the zone.
+ *
+ * The returned zone is equivalent to `this` (and frequently is indeed
+ * `this`).
+ *
+ * The main purpose of this method is to allow `this` to attach debugging
+ * information to the returned zone.
+ */
+ _Zone fork();
+
+ /**
+ * Tells the zone that it needs to wait for one more callback before it is
+ * done.
+ *
+ * Use [executeCallback] or [cancelCallbackExpectation] when the callback is executed
+ * (or canceled).
+ */
+ void expectCallback();
+
+ /**
+ * Tells the zone not to wait for a callback anymore.
+ *
+ * Prefer calling [executeCallback], instead. This method is mostly useful
+ * for repeated callbacks (for example with [Timer.periodic]). In this case
+ * one should should call [expectCallback] when the repeated callback is
+ * initiated, and [cancelCallbackExpectation] when the [Timer] is canceled.
+ */
+ void cancelCallbackExpectation();
+
+ /**
+ * Executes the given callback in this zone.
+ *
+ * Decrements the number of callbacks this zone is waiting for (see
+ * [expectCallback]).
+ */
+ void executeCallback(void fun());
+
+ /**
+ * Same as [executeCallback] but catches uncaught errors and gives them to
+ * [handleUncaughtError].
+ */
+ void executeCallbackGuarded(void fun());
+
+ /**
+ * Same as [executeCallback] but does not decrement the number of
+ * callbacks this zone is waiting for (see [expectCallback]).
+ */
+ void executePeriodicCallback(void fun());
+
+ /**
+ * Same as [executePeriodicCallback] but catches uncaught errors and gives
+ * them to [handleUncaughtError].
+ */
+ void executePeriodicCallbackGuarded(void fun());
+
+ /**
+ * Runs [fun] asynchronously in this zone.
+ */
+ void runAsync(void fun());
+
+ /**
+ * Creates a Timer where the callback is executed in this zone.
+ */
+ Timer createTimer(Duration duration, void callback());
+
+ /**
+ * Creates a periodic Timer where the callback is executed in this zone.
+ */
+ Timer createPeriodicTimer(Duration duration, void callback(Timer timer));
+
+ /**
+ * The error zone is the one that is responsible for dealing with uncaught
+ * errors. Errors are not allowed to cross zones with different error-zones.
+ */
+ _Zone get _errorZone;
+
+ /**
+ * Adds [child] as a child of `this`.
+ *
+ * This usually means that the [child] is in the asynchronous dynamic extent
+ * of `this`.
+ */
+ void _addChild(_Zone child);
+
+ /**
+ * Removes [child] from `this`' children.
+ *
+ * This usually means that the [child] has finished executing and is done.
+ */
+ void _removeChild(_Zone child);
+}
+
+/**
+ * Basic implementation of a [_Zone]. This class is intended for subclassing.
+ */
+class _ZoneBase implements _Zone {
+ /// The parent zone. [null] if `this` is the default zone.
+ final _Zone _parentZone;
+
+ /// The children of this zone. A child's [_parentZone] is `this`.
+ // TODO(floitsch): this should be a double-linked list.
+ final List<_Zone> _children = <_Zone>[];
+
+ /// The number of outstanding (asynchronous) callbacks. As long as the
+ /// number is greater than 0 it means that the zone is not done yet.
+ int _openCallbacks = 0;
+
+ bool _isExecutingCallback = false;
+
+ _ZoneBase(this._parentZone) {
+ _parentZone._addChild(this);
+ }
+
+ _ZoneBase._defaultZone() : _parentZone = null {
+ assert(this is _DefaultZone);
+ }
+
+ _Zone get _errorZone => _parentZone._errorZone;
+
+ void handleUncaughtError(error) {
+ _parentZone.handleUncaughtError(error);
+ }
+
+ bool inSameErrorZone(_Zone otherZone) => _errorZone == otherZone._errorZone;
+
+ _Zone fork() => this;
+
+ expectCallback() => _openCallbacks++;
+
+ cancelCallbackExpectation() {
+ _openCallbacks--;
+ _checkIfDone();
+ }
+
+ /**
+ * Cleans up this zone when it is done.
+ *
+ * This releases internal memore structures that are no longer necessary.
+ *
+ * A zone is done when its dynamic extent has finished executing and
+ * there are no outstanding asynchronous callbacks.
+ */
+ _dispose() {
+ if (_parentZone != null) {
+ _parentZone._removeChild(this);
+ }
+ }
+
+ /**
+ * Checks if the zone is done and doesn't have any outstanding callbacks
+ * anymore.
+ *
+ * This method is called when an operation has decremented the
+ * outstanding-callback count, or when a child has been removed.
+ */
+ void _checkIfDone() {
+ if (!_isExecutingCallback && _openCallbacks == 0 && _children.isEmpty) {
+ _dispose();
+ }
+ }
+
+ /**
+ * Executes the given callback in this zone.
+ *
+ * Decrements the open-callback counter and checks (after the call) if the
+ * zone is done.
+ */
+ void executeCallback(void fun()) {
+ _openCallbacks--;
+ this._runUnguarded(fun);
+ }
+
+ /**
+ * Same as [executeCallback] but catches uncaught errors and gives them to
+ * [handleUncaughtError].
+ */
+ void executeCallbackGuarded(void fun()) {
+ _openCallbacks--;
+ this._runGuarded(fun);
+ }
+
+ /**
+ * Same as [executeCallback] but doesn't decrement the open-callback counter.
+ */
+ void executePeriodicCallback(void fun()) {
+ this._runUnguarded(fun);
+ }
+
+ /**
+ * Same as [executePeriodicCallback] but catches uncaught errors and gives
+ * them to [handleUncaughtError].
+ */
+ void executePeriodicCallbackGuarded(void fun()) {
+ this._runGuarded(fun);
+ }
+
+ _runInZone(fun(), bool handleUncaught) {
+ if (identical(_Zone._current, this)
+ && !handleUncaught
+ && _isExecutingCallback) {
+ // No need to go through a try/catch.
+ return fun();
+ }
+
+ _Zone oldZone = _Zone._current;
+ _Zone._current = this;
+ // While we are executing the function we don't want to have other
+ // synchronous calls to think that they closed the zone. By incrementing
+ // the _openCallbacks count we make sure that their test will fail.
+ // As a side effect it will make nested calls faster since they are
+ // (probably) in the same zone and have an _openCallbacks > 0.
+ bool oldIsExecuting = _isExecutingCallback;
+ _isExecutingCallback = true;
+ // TODO(430): remove second try when VM bug is fixed.
+ try {
+ try {
+ return fun();
+ } catch(e, s) {
+ if (handleUncaught) {
+ handleUncaughtError(_asyncError(e, s));
+ } else {
+ rethrow;
+ }
+ }
+ } finally {
+ _isExecutingCallback = oldIsExecuting;
+ _Zone._current = oldZone;
+ _checkIfDone();
+ }
+ }
+
+ /**
+ * Runs the function and catches uncaught errors.
+ *
+ * Uncaught errors are given to [handleUncaughtError].
+ */
+ _runGuarded(void fun()) {
+ return _runInZone(fun, true);
+ }
+
+ /**
+ * Runs the function but doesn't catch uncaught errors.
+ */
+ _runUnguarded(void fun()) {
+ return _runInZone(fun, false);
+ }
+
+ runAsync(void fun()) {
+ _openCallbacks++;
+ _scheduleAsyncCallback(() {
+ _openCallbacks--;
+ _runGuarded(fun);
+ });
+ }
+
+ Timer createTimer(Duration duration, void callback()) {
+ return new _ZoneTimer(this, duration, callback);
+ }
+
+ Timer createPeriodicTimer(Duration duration, void callback(Timer timer)) {
+ return new _PeriodicZoneTimer(this, duration, callback);
+ }
+
+ void _addChild(_Zone child) {
+ _children.add(child);
+ }
+
+ void _removeChild(_Zone child) {
+ assert(!_children.isEmpty);
+ // Children are usually added and removed fifo or filo.
+ if (identical(_children.last, child)) {
+ _children.length--;
+ _checkIfDone();
+ return;
+ }
+ for (int i = 0; i < _children.length; i++) {
+ if (identical(_children[i], child)) {
+ _children[i] = _children[_children.length - 1];
+ _children.length--;
+ // No need to check for done, as otherwise _children.last above would
+ // have triggered.
+ assert(!_children.isEmpty);
+ return;
+ }
+ }
+ throw new ArgumentError(child);
+ }
+}
+
+/**
+ * The default-zone that conceptually surrounds the `main` function.
+ */
+class _DefaultZone extends _ZoneBase {
+ _DefaultZone() : super._defaultZone();
+
+ _Zone get _errorZone => this;
+
+ handleUncaughtError(error) {
+ _scheduleAsyncCallback(() {
+ print("Uncaught Error: ${error}");
+ var trace = getAttachedStackTrace(error);
+ _attachStackTrace(error, null);
+ if (trace != null) {
+ print("Stack Trace:\n$trace\n");
+ }
+ throw error;
+ });
+ }
+}
+
+typedef void _CompletionCallback();
+
+/**
+ * A zone that executes a callback when the zone is dead.
+ */
+class _WaitForCompletionZone extends _ZoneBase {
+ final _CompletionCallback _onDone;
+
+ _WaitForCompletionZone(_Zone parentZone, this._onDone) : super(parentZone);
+
+ /**
+ * Runs the given function asynchronously. Executes the [_onDone] callback
+ * when the zone is done.
+ */
+ runWaitForCompletion(void fun()) {
+ return this._runUnguarded(fun);
+ }
+
+ _dispose() {
+ super._dispose();
+ _onDone();
+ }
+
+ String toString() => "WaitForCompletion ${super.toString()}";
+}
+
+typedef void _HandleErrorCallback(error);
+
+/**
+ * A zone that collects all uncaught errors and provides them in a stream.
+ * The stream is closed when the zone is done.
+ */
+class _CatchErrorsZone extends _WaitForCompletionZone {
+ final _HandleErrorCallback _handleError;
+
+ _CatchErrorsZone(_Zone parentZone, this._handleError, void onDone())
+ : super(parentZone, onDone);
+
+ _Zone get _errorZone => this;
+
+ handleUncaughtError(error) {
+ try {
+ _handleError(error);
+ } catch(e, s) {
+ if (identical(e, s)) {
+ _parentZone.handleUncaughtError(error);
+ } else {
+ _parentZone.handleUncaughtError(_asyncError(e, s));
+ }
+ }
+ }
+
+ /**
+ * Runs the given function asynchronously. Executes the [_onDone] callback
+ * when the zone is done.
+ */
+ runWaitForCompletion(void fun()) {
+ return this._runGuarded(fun);
+ }
+
+ String toString() => "WithErrors ${super.toString()}";
+}
+
+typedef void _TimerCallback();
+
+/**
+ * A [Timer] class that takes zones into account.
+ */
+class _ZoneTimer implements Timer {
+ final _Zone _zone;
+ final _TimerCallback _callback;
+ Timer _timer;
+ bool _isDone = false;
+
+ _ZoneTimer(this._zone, Duration duration, this._callback) {
+ _zone.expectCallback();
+ _timer = _createTimer(duration, this.run);
+ }
+
+ void run() {
+ _isDone = true;
+ _zone.executeCallbackGuarded(_callback);
+ }
+
+ void cancel() {
+ if (!_isDone) _zone.cancelCallbackExpectation();
+ _isDone = true;
+ _timer.cancel();
+ }
+}
+
+typedef void _PeriodicTimerCallback(Timer timer);
+
+/**
+ * A [Timer] class for periodic callbacks that takes zones into account.
+ */
+class _PeriodicZoneTimer implements Timer {
+ final _Zone _zone;
+ final _PeriodicTimerCallback _callback;
+ Timer _timer;
+ bool _isDone = false;
+
+ _PeriodicZoneTimer(this._zone, Duration duration, this._callback) {
+ _zone.expectCallback();
+ _timer = _createPeriodicTimer(duration, this.run);
+ }
+
+ void run(Timer timer) {
+ assert(identical(_timer, timer));
+ _zone.executePeriodicCallbackGuarded(() { _callback(this); });
+ }
+
+ void cancel() {
+ if (!_isDone) _zone.cancelCallbackExpectation();
+ _isDone = true;
+ _timer.cancel();
+ }
+}
+
+/**
+ * Runs [body] in its own zone.
+ *
+ * If [onError] is non-null the zone is considered an error zone. All uncaught
+ * errors, synchronous or asynchronous, in the zone are caught and handled
+ * by the callback.
+ *
+ * [onDone] (if non-null) is invoked when the zone has no more outstanding
+ * callbacks.
+ *
+ * Examples:
+ *
+ * runZonedExperimental(() {
+ * new Future(() { throw "asynchronous error"; });
+ * }, onError: print); // Will print "asynchronous error".
+ *
+ * The following example prints "1", "2", "3", "4" in this order.
+ *
+ * runZonedExperimental(() {
+ * print(1);
+ * new Future.value(3).then(print);
+ * }, onDone: () { print(4); });
+ * print(2);
+ *
+ * Errors may never cross error-zone boundaries. This is intuitive for leaving
+ * a zone, but it also applies for errors that would enter an error-zone.
+ * Errors that try to cross error-zone boundaries are considered uncaught.
+ *
+ * var future = new Future.value(499);
+ * runZonedExperimental(() {
+ * future = future.then((_) { throw "error in first error-zone"; });
+ * runZonedExperimental(() {
+ * future = future.catchError((e) { print("Never reached!"); });
+ * }, onError: (e) { print("unused error handler"); });
+ * }, onError: (e) { print("catches error of first error-zone."); });
+ *
+ */
+runZonedExperimental(body(), { void onError(error), void onDone() }) {
+ // TODO(floitsch): we probably still want to install a new Zone.
+ if (onError == null && onDone == null) return body();
+ if (onError == null) {
+ _WaitForCompletionZone zone =
+ new _WaitForCompletionZone(_Zone._current, onDone);
+ return zone.runWaitForCompletion(body);
+ }
+ if (onDone == null) onDone = _nullDoneHandler;
+ _CatchErrorsZone zone = new _CatchErrorsZone(_Zone._current, onError, onDone);
+ return zone.runWaitForCompletion(body);
+}
diff --git a/sdk/lib/io/directory_impl.dart b/sdk/lib/io/directory_impl.dart
index cdb6df4..f47ef4f 100644
--- a/sdk/lib/io/directory_impl.dart
+++ b/sdk/lib/io/directory_impl.dart
@@ -9,8 +9,10 @@
static const DELETE_REQUEST = 1;
static const EXISTS_REQUEST = 2;
static const CREATE_TEMP_REQUEST = 3;
- static const LIST_REQUEST = 4;
- static const RENAME_REQUEST = 5;
+ static const LIST_START_REQUEST = 4;
+ static const LIST_NEXT_REQUEST = 5;
+ static const LIST_STOP_REQUEST = 6;
+ static const RENAME_REQUEST = 7;
_Directory(String this._path);
_Directory.fromPath(Path path) : this(path.toNativePath());
@@ -186,9 +188,6 @@
return new Directory(result);
}
- Future<Directory> _deleteHelper(bool recursive, String errorMsg) {
- }
-
Future<Directory> delete({recursive: false}) {
_ensureDirectoryService();
List request = new List(3);
@@ -240,68 +239,7 @@
Stream<FileSystemEntity> list({bool recursive: false,
bool followLinks: true}) {
- const int LIST_FILE = 0;
- const int LIST_DIRECTORY = 1;
- const int LIST_LINK = 2;
- const int LIST_ERROR = 3;
- const int LIST_DONE = 4;
-
- const int RESPONSE_TYPE = 0;
- const int RESPONSE_PATH = 1;
- const int RESPONSE_COMPLETE = 1;
- const int RESPONSE_ERROR = 2;
-
- var controller = new StreamController<FileSystemEntity>(sync: true);
-
- List request = [ _Directory.LIST_REQUEST, path, recursive, followLinks ];
- ReceivePort responsePort = new ReceivePort();
- // Use a separate directory service port for each listing as
- // listing operations on the same directory can run in parallel.
- _Directory._newServicePort().send(request, responsePort.toSendPort());
- responsePort.receive((message, replyTo) {
- if (message is !List || message[RESPONSE_TYPE] is !int) {
- responsePort.close();
- controller.addError(new DirectoryException("Internal error"));
- return;
- }
- switch (message[RESPONSE_TYPE]) {
- case LIST_FILE:
- controller.add(new File(message[RESPONSE_PATH]));
- break;
- case LIST_DIRECTORY:
- controller.add(new Directory(message[RESPONSE_PATH]));
- break;
- case LIST_LINK:
- controller.add(new Link(message[RESPONSE_PATH]));
- break;
- case LIST_ERROR:
- var errorType =
- message[RESPONSE_ERROR][_ERROR_RESPONSE_ERROR_TYPE];
- if (errorType == _ILLEGAL_ARGUMENT_RESPONSE) {
- controller.addError(new ArgumentError());
- } else if (errorType == _OSERROR_RESPONSE) {
- var responseError = message[RESPONSE_ERROR];
- var err = new OSError(
- responseError[_OSERROR_RESPONSE_MESSAGE],
- responseError[_OSERROR_RESPONSE_ERROR_CODE]);
- var errorPath = message[RESPONSE_PATH];
- if (errorPath == null) errorPath = path;
- controller.addError(
- new DirectoryException("Directory listing failed",
- errorPath,
- err));
- } else {
- controller.addError(new DirectoryException("Internal error"));
- }
- break;
- case LIST_DONE:
- responsePort.close();
- controller.close();
- break;
- }
- });
-
- return controller.stream;
+ return new _AsyncDirectoryLister(path, recursive, followLinks).stream;
}
List listSync({bool recursive: false, bool followLinks: true}) {
@@ -342,3 +280,135 @@
final String _path;
SendPort _directoryService;
}
+
+class _AsyncDirectoryLister {
+ static const int LIST_FILE = 0;
+ static const int LIST_DIRECTORY = 1;
+ static const int LIST_LINK = 2;
+ static const int LIST_ERROR = 3;
+ static const int LIST_DONE = 4;
+
+ static const int RESPONSE_TYPE = 0;
+ static const int RESPONSE_PATH = 1;
+ static const int RESPONSE_COMPLETE = 1;
+ static const int RESPONSE_ERROR = 2;
+
+ final String path;
+ final bool recursive;
+ final bool followLinks;
+
+ StreamController controller;
+ int id;
+ bool canceled = false;
+ bool nextRunning = false;
+ bool closed = false;
+
+ _AsyncDirectoryLister(String this.path,
+ bool this.recursive,
+ bool this.followLinks) {
+ controller = new StreamController(onListen: onListen,
+ onResume: onResume,
+ onCancel: onCancel);
+ }
+
+ Stream get stream => controller.stream;
+
+ void onListen() {
+ var request = [_Directory.LIST_START_REQUEST, path, recursive, followLinks];
+ _Directory._newServicePort().call(request)
+ .then((response) {
+ if (response is int) {
+ id = response;
+ next();
+ } else {
+ error(response);
+ controller.close();
+ }
+ });
+ }
+
+ void onResume() {
+ if (!nextRunning) next();
+ }
+
+ void onCancel() {
+ canceled = true;
+ // If we are active, but not requesting, close.
+ if (!nextRunning) {
+ close();
+ }
+ }
+
+ void next() {
+ if (canceled) {
+ close();
+ return;
+ }
+ if (id == null) return;
+ if (controller.isPaused) return;
+ assert(!nextRunning);
+ nextRunning = true;
+ _Directory._newServicePort().call([_Directory.LIST_NEXT_REQUEST, id])
+ .then((result) {
+ if (result is List) {
+ assert(result.length % 2 == 0);
+ for (int i = 0; i < result.length; i++) {
+ assert(i % 2 == 0);
+ switch (result[i++]) {
+ case LIST_FILE:
+ controller.add(new File(result[i]));
+ break;
+ case LIST_DIRECTORY:
+ controller.add(new Directory(result[i]));
+ break;
+ case LIST_LINK:
+ controller.add(new Link(result[i]));
+ break;
+ case LIST_ERROR:
+ error(result[i]);
+ break;
+ case LIST_DONE:
+ close();
+ return;
+ }
+ }
+ } else {
+ controller.addError(new DirectoryException("Internal error"));
+ }
+ nextRunning = false;
+ next();
+ });
+ }
+
+ void close() {
+ if (closed) return;
+ if (id == null) return;
+ closed = true;
+ _Directory._newServicePort().call([_Directory.LIST_STOP_REQUEST, id])
+ .then((_) {
+ controller.close();
+ });
+ }
+
+ void error(message) {
+ var errorType =
+ message[RESPONSE_ERROR][_ERROR_RESPONSE_ERROR_TYPE];
+ if (errorType == _ILLEGAL_ARGUMENT_RESPONSE) {
+ controller.addError(new ArgumentError());
+ } else if (errorType == _OSERROR_RESPONSE) {
+ var responseError = message[RESPONSE_ERROR];
+ var err = new OSError(
+ responseError[_OSERROR_RESPONSE_MESSAGE],
+ responseError[_OSERROR_RESPONSE_ERROR_CODE]);
+ var errorPath = message[RESPONSE_PATH];
+ if (errorPath == null) errorPath = path;
+ controller.addError(
+ new DirectoryException("Directory listing failed",
+ errorPath,
+ err));
+ } else {
+ controller.addError(
+ new DirectoryException("Internal error"));
+ }
+ }
+}
diff --git a/tests/compiler/dart2js/concrete_type_inference_test.dart b/tests/compiler/dart2js/concrete_type_inference_test.dart
index 581b26b..8284448 100644
--- a/tests/compiler/dart2js/concrete_type_inference_test.dart
+++ b/tests/compiler/dart2js/concrete_type_inference_test.dart
@@ -63,6 +63,7 @@
});
checkPrintType('[]', (compiler, type) {
var inferrer = compiler.typesTask.typesInferrer;
+ if (type.isContainer) type = type.asFlat;
Expect.identical(inferrer.growableListType, type);
});
checkPrintType('null', (compiler, type) {
diff --git a/tests/compiler/dart2js/list_tracer_test.dart b/tests/compiler/dart2js/list_tracer_test.dart
new file mode 100644
index 0000000..a82cac8
--- /dev/null
+++ b/tests/compiler/dart2js/list_tracer_test.dart
@@ -0,0 +1,220 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 'package:expect/expect.dart';
+import
+ '../../../sdk/lib/_internal/compiler/implementation/types/types.dart'
+ show ContainerTypeMask, TypeMask;
+
+import 'compiler_helper.dart';
+import 'parser_helper.dart';
+
+
+String generateTest(String listAllocation) {
+ return """
+int anInt = 42;
+double aDouble = 42.5;
+
+class A {
+ final field;
+ var nonFinalField;
+
+ A(this.field);
+
+ A.bar(list) {
+ nonFinalField = list;
+ }
+
+ receiveIt(list) {
+ list[0] = aDouble;
+ }
+
+ returnIt() {
+ return listReturnedFromSelector;
+ }
+
+ useField() {
+ field[0] = aDouble;
+ }
+
+ set callSetter(list) {
+ list[0] = aDouble;
+ }
+
+ operator[](index) {
+ index[0] = aDouble;
+ }
+
+ operator[]=(index, value) {
+ index[0] = anInt;
+ if (value == listEscapingTwiceInIndexSet) {
+ value[0] = aDouble;
+ }
+ }
+}
+
+class B extends A {
+ B(list) : super.bar(list);
+
+ set nonFinalField(value) {
+ value[0] = aDouble;
+ }
+}
+
+var listInField = $listAllocation;
+var listPassedToClosure = $listAllocation;
+var listReturnedFromClosure = $listAllocation;
+var listPassedToMethod = $listAllocation;
+var listReturnedFromMethod = $listAllocation;
+var listUsedWithCascade = $listAllocation;
+var listUsedInClosure = $listAllocation;
+var listPassedToSelector = $listAllocation;
+var listReturnedFromSelector = $listAllocation;
+var listUsedWithAddAndInsert = $listAllocation;
+var listUsedWithNonOkSelector = $listAllocation;
+var listUsedWithConstraint = $listAllocation;
+var listEscapingFromSetter = $listAllocation;
+var listUsedInLocal = $listAllocation;
+var listUnset = $listAllocation;
+var listOnlySetWithConstraint = $listAllocation;
+var listEscapingInSetterValue = $listAllocation;
+var listEscapingInIndex = $listAllocation;
+var listEscapingInIndexSet = $listAllocation;
+var listEscapingTwiceInIndexSet = $listAllocation;
+var listPassedAsOptionalParameter = $listAllocation;
+var listPassedAsNamedParameter = $listAllocation;
+var listSetInNonFinalField = $listAllocation;
+
+foo(list) {
+ list[0] = aDouble;
+}
+
+bar() {
+ return listReturnedFromMethod;
+}
+
+takeOptional([list]) {
+ list[0] = aDouble;
+}
+
+takeNamed({list}) {
+ list[0] = aDouble;
+}
+
+main() {
+ listReturnedFromMethod[0] = anInt;
+ bar()[0] = aDouble;
+
+ listPassedToMethod[0] = anInt;
+ foo(listPassedToMethod);
+
+ listPassedToClosure[0] = anInt;
+ ((a) => a[0] = aDouble)(listPassedToClosure);
+
+ listReturnedFromClosure[0] = anInt;
+ (() => listReturnedFromClosure)[0] = aDouble;
+
+ listInField[0] = anInt;
+ new A(listInField).useField();
+
+ listUsedWithCascade[0] = anInt;
+ listUsedWithCascade..[0] = aDouble;
+
+ listUsedInClosure[0] = anInt;
+ (() => listUsedInClosure[0] = aDouble)();
+
+ listPassedToSelector[0] = anInt;
+ new A(null).receiveIt(listPassedToSelector);
+
+ listReturnedFromSelector[0] = anInt;
+ new A(null).returnIt()[0] = aDouble;
+
+ listUsedWithAddAndInsert.add(anInt);
+ listUsedWithAddAndInsert.insert(0, aDouble);
+
+ listUsedWithNonOkSelector[0] = anInt;
+ listUsedWithNonOkSelector.addAll(listPassedToClosure);
+
+ listUsedWithConstraint[0] = anInt;
+ listUsedWithConstraint[0]++;
+ listUsedWithConstraint[0] += anInt;
+
+ listEscapingFromSetter[0] = anInt;
+ foo(new A(null).field = listEscapingFromSetter);
+
+ listUsedInLocal[0] = anInt;
+ var a = listUsedInLocal;
+ listUsedInLocal[1] = aDouble;
+
+ // At least use [listUnused] in a local to pretend it's used.
+ var b = listUnset;
+
+ listOnlySetWithConstraint[0]++;
+
+ listEscapingInSetterValue[0] = anInt;
+ new A().callSetter = listEscapingInSetterValue;
+
+ listEscapingInIndex[0] = anInt;
+ new A()[listEscapingInIndex];
+
+ new A()[listEscapingInIndexSet] = 42;
+
+ new A()[listEscapingTwiceInIndexSet] = listEscapingTwiceInIndexSet;
+
+ listPassedAsOptionalParameter[0] = anInt;
+ takeOptional(listPassedAsOptionalParameter);
+
+ listPassedAsNamedParameter[0] = anInt;
+ takeNamed(list: listPassedAsNamedParameter);
+
+ listSetInNonFinalField[0] = anInt;
+ new B(listSetInNonFinalField);
+}
+""";
+}
+
+void main() {
+ doTest('[]'); // Test literal list.
+ doTest('new List()'); // Test growable list.
+ doTest('new List(1)'); // Test fixed list.
+}
+
+void doTest(String allocation) {
+ Uri uri = new Uri(scheme: 'source');
+ var compiler = compilerFor(generateTest(allocation), uri);
+ compiler.runCompiler(uri);
+ var typesInferrer = compiler.typesTask.typesInferrer;
+
+ checkType(String name, type) {
+ var element = findElement(compiler, name);
+ ContainerTypeMask mask = typesInferrer.internal.typeOf[element];
+ Expect.equals(type, mask.elementType.simplify(compiler), name);
+ }
+
+ checkType('listInField', typesInferrer.numType.nullable());
+ checkType('listPassedToMethod', typesInferrer.numType.nullable());
+ checkType('listReturnedFromMethod', typesInferrer.numType.nullable());
+ checkType('listUsedWithCascade', typesInferrer.numType.nullable());
+ checkType('listUsedInClosure', typesInferrer.numType.nullable());
+ checkType('listPassedToSelector', typesInferrer.numType.nullable());
+ checkType('listReturnedFromSelector', typesInferrer.numType.nullable());
+ checkType('listUsedWithAddAndInsert', typesInferrer.numType.nullable());
+ checkType('listUsedWithConstraint', typesInferrer.numType.nullable());
+ checkType('listEscapingFromSetter', typesInferrer.numType.nullable());
+ checkType('listUsedInLocal', typesInferrer.numType.nullable());
+ checkType('listEscapingInSetterValue', typesInferrer.numType.nullable());
+ checkType('listEscapingInIndex', typesInferrer.numType.nullable());
+ checkType('listEscapingInIndexSet', typesInferrer.intType.nullable());
+ checkType('listEscapingTwiceInIndexSet', typesInferrer.numType.nullable());
+ checkType('listSetInNonFinalField', typesInferrer.numType.nullable());
+
+ checkType('listPassedToClosure', typesInferrer.dynamicType);
+ checkType('listReturnedFromClosure', typesInferrer.dynamicType);
+ checkType('listUsedWithNonOkSelector', typesInferrer.dynamicType);
+ checkType('listPassedAsOptionalParameter', typesInferrer.dynamicType);
+ checkType('listPassedAsNamedParameter', typesInferrer.dynamicType);
+
+ checkType('listUnset', new TypeMask.empty());
+ checkType('listOnlySetWithConstraint', new TypeMask.empty());
+}
diff --git a/tests/compiler/dart2js/mirror_tree_shaking_test.dart b/tests/compiler/dart2js/mirror_tree_shaking_test.dart
index 7025c41..4869c70 100644
--- a/tests/compiler/dart2js/mirror_tree_shaking_test.dart
+++ b/tests/compiler/dart2js/mirror_tree_shaking_test.dart
@@ -44,6 +44,7 @@
Expect.isFalse(compiler.compilationFailed);
Expect.isFalse(compiler.enqueuer.resolution.hasEnqueuedEverything);
Expect.isFalse(compiler.enqueuer.codegen.hasEnqueuedEverything);
+ Expect.isFalse(compiler.disableTypeInference);
}
const Map MEMORY_SOURCE_FILES = const {
diff --git a/tests/compiler/dart2js/mock_compiler.dart b/tests/compiler/dart2js/mock_compiler.dart
index 4d80094..9fde6d6 100644
--- a/tests/compiler/dart2js/mock_compiler.dart
+++ b/tests/compiler/dart2js/mock_compiler.dart
@@ -65,6 +65,7 @@
class ConstantMap {}
class TypeImpl {}
S() {}
+ throwCyclicInit() {}
throwExpression(e) {}
unwrapException(e) {}
assertHelper(a) {}
@@ -167,7 +168,7 @@
abstract class StackTrace {}
class Type {}
class Function {}
- class List<E> {}
+ class List<E> { List([length]); }
abstract class Map<K,V> {}
class DateTime {
DateTime(year);
diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status
index 6e91831..6bd2384 100644
--- a/tests/corelib/corelib.status
+++ b/tests/corelib/corelib.status
@@ -105,7 +105,7 @@
list_removeat_test: fail
[ $arch == simarm && $mode == debug ]
-collection_to_string_test: Crash # Issue: 11207
+collection_to_string_test: Pass, Crash # Issue: 11207
[ $arch == mips ]
*: Skip
diff --git a/tests/language/language.status b/tests/language/language.status
index 7554f25..e55fc34 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -553,4 +553,17 @@
*: Skip
[ $arch == simmips ]
-*: Skip
+arithmetic_test: Fail, Crash
+bit_operations_test: Crash, Fail
+char_escape_test: Pass, Crash
+deopt_smi_op_test: Fail
+div_with_power_of_two_test: Fail
+gc_test: Crash
+invocation_mirror_test: Fail
+load_to_load_forwarding_vm_test: Fail
+named_parameters_with_conversions_test: Pass, Crash
+positive_bit_operations_test: Pass, Fail, Crash
+left_shift_test: Pass, Fail
+large_implicit_getter_test: Crash, Pass
+stack_overflow_test: Crash, Pass
+stack_overflow_stacktrace_test: Crash, Pass
diff --git a/tests/lib/async/catch_errors.dart b/tests/lib/async/catch_errors.dart
new file mode 100644
index 0000000..c289690
--- /dev/null
+++ b/tests/lib/async/catch_errors.dart
@@ -0,0 +1,29 @@
+library catch_errors;
+
+import 'dart:async';
+
+Stream catchErrors(void body()) {
+ StreamController controller;
+
+ bool onError(e) {
+ controller.add(e);
+ return true;
+ }
+
+ void onDone() {
+ controller.close();
+ }
+
+ void onListen() {
+ runZonedExperimental(body, onError: onError, onDone: onDone);
+ }
+
+ controller = new StreamController(onListen: onListen);
+ return controller.stream;
+}
+
+Future waitForCompletion(void body()) {
+ Completer completer = new Completer.sync();
+ runZonedExperimental(body, onDone: completer.complete);
+ return completer.future;
+}
diff --git a/tests/lib/async/catch_errors10_test.dart b/tests/lib/async/catch_errors10_test.dart
new file mode 100644
index 0000000..b554dbd
--- /dev/null
+++ b/tests/lib/async/catch_errors10_test.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ bool futureWasExecuted = false;
+ // Test that `catchErrors` waits for a future that has been delayed by
+ // `Timer.run`.
+ catchErrors(() {
+ Timer.run(() {
+ new Future.value(499).then((x) {
+ futureWasExecuted = true;
+ });
+ });
+ return 'allDone';
+ }).listen((x) {
+ Expect.fail("Unexpected callback");
+ },
+ onDone: () {
+ Expect.isTrue(futureWasExecuted);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors11_test.dart b/tests/lib/async/catch_errors11_test.dart
new file mode 100644
index 0000000..53755261
--- /dev/null
+++ b/tests/lib/async/catch_errors11_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Test that `catchErrors` catches errors that are delayed by `Timer.run`.
+ catchErrors(() {
+ events.add("catch error entry");
+ new Future.error("future error");
+ Timer.run(() { throw "timer error"; });
+ }).listen((x) {
+ events.add(x);
+ },
+ onDone: () {
+ Expect.listEquals([
+ "catch error entry",
+ "main exit",
+ "future error",
+ "timer error",
+ ],
+ events);
+ port.close();
+ });
+ events.add("main exit");
+}
diff --git a/tests/lib/async/catch_errors12_test.dart b/tests/lib/async/catch_errors12_test.dart
new file mode 100644
index 0000000..deb7cb4
--- /dev/null
+++ b/tests/lib/async/catch_errors12_test.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Tests that errors that have been delayed by several milliseconds with
+ // Timers are still caught by `catchErrors`.
+ catchErrors(() {
+ events.add("catch error entry");
+ Timer.run(() { throw "timer error"; });
+ new Timer(const Duration(milliseconds: 100), () { throw "timer2 error"; });
+ new Future.value(499).then((x) {
+ new Timer(const Duration(milliseconds: 200), () { throw x; });
+ });
+ throw "catch error";
+ }).listen((x) {
+ events.add(x);
+ },
+ onDone: () {
+ Expect.listEquals([
+ "catch error entry",
+ "main exit",
+ "catch error",
+ "timer error",
+ "timer2 error",
+ 499,
+ ],
+ events);
+ port.close();
+ });
+ events.add("main exit");
+}
diff --git a/tests/lib/async/catch_errors13_test.dart b/tests/lib/async/catch_errors13_test.dart
new file mode 100644
index 0000000..7c6752a
--- /dev/null
+++ b/tests/lib/async/catch_errors13_test.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Work around bug that makes runAsync use Timers. By invoking `runAsync` here
+ // we make sure that asynchronous non-timer events are executed before any
+ // Timer events.
+ runAsync(() { });
+
+ // Test that errors are caught by nested `catchErrors`. Also uses `runAsync`
+ // in the body of a Timer.
+ catchErrors(() {
+ events.add("catch error entry");
+ catchErrors(() {
+ events.add("catch error entry2");
+ Timer.run(() { throw "timer error"; });
+ new Timer(const Duration(milliseconds: 50),
+ () {
+ runAsync(() { throw "runAsync"; });
+ throw "delayed error";
+ });
+ }).listen((x) { events.add(x); })
+ .asFuture()
+ .then((_) => events.add("inner done"))
+ .then((_) { throw "inner done throw"; });
+ events.add("after inner");
+ Timer.run(() { throw "timer outer"; });
+ throw "inner throw";
+ }).listen((x) {
+ events.add(x);
+ },
+ onDone: () {
+ Expect.listEquals([
+ "catch error entry",
+ "catch error entry2",
+ "after inner",
+ "main exit",
+ "inner throw",
+ "timer error",
+ "timer outer",
+ "delayed error",
+ "runAsync",
+ "inner done",
+ "inner done throw"
+ ],
+ events);
+ port.close();
+ });
+ events.add("main exit");
+}
diff --git a/tests/lib/async/catch_errors14_test.dart b/tests/lib/async/catch_errors14_test.dart
new file mode 100644
index 0000000..ebf34a1
--- /dev/null
+++ b/tests/lib/async/catch_errors14_test.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Test that periodic Timers are handled correctly by `catchErrors`.
+ catchErrors(() {
+ int counter = 0;
+ new Timer.periodic(const Duration(milliseconds: 50), (timer) {
+ if (counter++ == 5) timer.cancel();
+ throw "error $counter";
+ });
+ }).listen((x) {
+ events.add(x);
+ },
+ onDone: () {
+ Expect.listEquals([
+ "main exit",
+ "error 1",
+ "error 2",
+ "error 3",
+ "error 4",
+ "error 5",
+ "error 6",
+ ],
+ events);
+ port.close();
+ });
+ events.add("main exit");
+}
diff --git a/tests/lib/async/catch_errors15_test.dart b/tests/lib/async/catch_errors15_test.dart
new file mode 100644
index 0000000..2cea6fe
--- /dev/null
+++ b/tests/lib/async/catch_errors15_test.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Test that the outer `catchErrors` waits for the nested `catchErrors` stream
+ // to be done.
+ catchErrors(() {
+ events.add("catch error entry");
+ catchErrors(() {
+ events.add("catch error entry2");
+ new Future.error("future error");
+ new Future.error("future error2");
+ new Future.value(499).then((x) => throw x);
+ new Future.delayed(const Duration(milliseconds: 50), () {
+ throw "delayed error";
+ });
+ throw "catch error";
+ }).listen((x) { events.add("i $x"); },
+ onDone: () => events.add("inner done"));
+ events.add("after inner");
+ throw "inner throw";
+ }).listen((x) {
+ events.add("o $x");
+ },
+ onDone: () {
+ Expect.listEquals(["catch error entry",
+ "catch error entry2",
+ "after inner",
+ "main exit",
+ "i catch error",
+ "o inner throw",
+ "i future error",
+ "i future error2",
+ "i 499",
+ "i delayed error",
+ "inner done",
+ ],
+ events);
+ port.close();
+ });
+ events.add("main exit");
+}
diff --git a/tests/lib/async/catch_errors16_test.dart b/tests/lib/async/catch_errors16_test.dart
new file mode 100644
index 0000000..cb3621d
--- /dev/null
+++ b/tests/lib/async/catch_errors16_test.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ StreamController controller = new StreamController();
+ Stream stream = controller.stream;
+ // Test that the subscription of a stream is what counts. The error (2) runs
+ // through the map-stream which goes through the nested `catchError` but
+ // the nested `catchError` won't see the error.
+ catchErrors(() {
+ stream = stream.map((x) => x + 100);
+ }).listen((x) { events.add(x); });
+ stream
+ .transform(new StreamTransformer(
+ handleError: (e, sink) => sink.add("error $e")))
+ .listen((x) { events.add("stream $x"); },
+ onDone: () {
+ Expect.listEquals(["stream 101",
+ "stream error 2",
+ ],
+ events);
+ port.close();
+ });
+ controller.add(1);
+ controller.addError(2);
+ controller.close();
+}
diff --git a/tests/lib/async/catch_errors17_test.dart b/tests/lib/async/catch_errors17_test.dart
new file mode 100644
index 0000000..bf19b09
--- /dev/null
+++ b/tests/lib/async/catch_errors17_test.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ StreamController controller;
+ // Test that errors do not cross zone boundaries.
+ catchErrors(() {
+ catchErrors(() {
+ controller = new StreamController();
+ controller.stream
+ .map((x) {
+ events.add("map $x");
+ return x + 100;
+ })
+ .transform(new StreamTransformer(
+ handleError: (e, sink) => sink.add("error $e")))
+ .listen((x) { events.add("stream $x"); });
+ }).listen((x) { events.add(x); })
+ .asFuture().then((_) { events.add("inner done"); });
+ controller.add(1);
+ // Errors are not allowed to traverse boundaries. This error should be
+ // caught by the outer catchErrors.
+ controller.addError(2);
+ controller.close();
+ }).listen((x) { events.add("outer: $x"); },
+ onDone: () {
+ Expect.listEquals(["map 1",
+ "stream 101",
+ "outer: 2",
+ "inner done",
+ ],
+ events);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors18_test.dart b/tests/lib/async/catch_errors18_test.dart
new file mode 100644
index 0000000..33e60ab
--- /dev/null
+++ b/tests/lib/async/catch_errors18_test.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ Stream stream = new Stream.periodic(const Duration(milliseconds: 20),
+ (x) => x);
+ // Test that errors of periodic streams are caught.
+ catchErrors(() {
+ var subscription;
+ subscription = stream.listen((x) {
+ if (x == 5) {
+ events.add("cancel");
+ subscription.cancel();
+ return;
+ }
+ events.add(x);
+ });
+ }).listen((x) { events.add("outer: $x"); },
+ onDone: () {
+ Expect.listEquals([0, 1, 2, 3, 4, "cancel"], events);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors19_test.dart b/tests/lib/async/catch_errors19_test.dart
new file mode 100644
index 0000000..dec100a
--- /dev/null
+++ b/tests/lib/async/catch_errors19_test.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ Stream stream = new Stream.periodic(const Duration(milliseconds: 20),
+ (x) => x);
+ // Test that asynchronous callbacks in the done-handler of streams (here
+ // the `catchErrors`-stream) keep a zone alive.
+ catchErrors(() {
+ var subscription;
+ subscription = stream.take(5).listen((x) {
+ events.add(x);
+ }, onDone: () {
+ new Future.delayed(const Duration(milliseconds: 30), () {
+ events.add(499);
+ });
+ });
+ }).listen((x) { events.add("outer: $x"); },
+ onDone: () {
+ Expect.listEquals([0, 1, 2, 3, 4, 499], events);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors20_test.dart b/tests/lib/async/catch_errors20_test.dart
new file mode 100644
index 0000000..b0f0673
--- /dev/null
+++ b/tests/lib/async/catch_errors20_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Test that nested stream (from `catchErrors`) that is delayed by a future
+ // is waited for.
+ catchErrors(() {
+ catchErrors(() {
+ new Future.error(499);
+ new Future.delayed(const Duration(milliseconds: 20), () {
+ events.add(42);
+ });
+ }).listen(events.add,
+ onDone: () { events.add("done"); });
+ throw "foo";
+ }).listen((x) { events.add("outer: $x"); },
+ onDone: () {
+ Expect.listEquals(["outer: foo",
+ 499,
+ 42,
+ "done",
+ ],
+ events);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors21_test.dart b/tests/lib/async/catch_errors21_test.dart
new file mode 100644
index 0000000..28dec7f
--- /dev/null
+++ b/tests/lib/async/catch_errors21_test.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ var controller = new StreamController();
+ // Test that stream errors, as a result of bad user-code (`map`) are correctly
+ // caught by `catchErrors`. Note that the values are added outside both
+ // `catchErrors`, but since the `listen` happens in the most nested
+ // `catchErrors` it is caught there.
+ catchErrors(() {
+ catchErrors(() {
+ controller.stream.map((x) { throw x; }).listen((x) {
+ // Should not happen.
+ events.add("bad: $x");
+ });
+ }).listen((x) { events.add("caught: $x"); },
+ onDone: () { events.add("done"); });
+ }).listen((x) { events.add("outer: $x"); },
+ onDone: () {
+ Expect.listEquals(["caught: 1",
+ "caught: 2",
+ "caught: 3",
+ "caught: 4",
+ "done",
+ ],
+ events);
+ port.close();
+ });
+ [1, 2, 3, 4].forEach(controller.add);
+ controller.close();
+}
diff --git a/tests/lib/async/catch_errors22_test.dart b/tests/lib/async/catch_errors22_test.dart
new file mode 100644
index 0000000..c47b3a3
--- /dev/null
+++ b/tests/lib/async/catch_errors22_test.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ bool onDoneWasCalled = false;
+ var controller = new StreamController();
+ // Test that streams that are never closed keep the `catchError` alive.
+ catchErrors(() {
+ catchErrors(() {
+ controller.stream.map((x) { throw x; }).listen((x) {
+ // Should not happen.
+ events.add("bad: $x");
+ });
+ }).listen((x) { events.add("caught: $x"); },
+ onDone: () { events.add("done"); });
+ }).listen((x) { events.add("outer: $x"); },
+ onDone: () { onDoneWasCalled = true; });
+
+ [1, 2, 3, 4].forEach(controller.add);
+
+ new Timer(const Duration(milliseconds: 20), () {
+ Expect.isFalse(onDoneWasCalled);
+ Expect.listEquals(["caught: 1",
+ "caught: 2",
+ "caught: 3",
+ "caught: 4",
+ ],
+ events);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors23_test.dart b/tests/lib/async/catch_errors23_test.dart
new file mode 100644
index 0000000..2d2ff72
--- /dev/null
+++ b/tests/lib/async/catch_errors23_test.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ StreamController controller;
+ Stream stream;
+ // Test that errors are not traversing zone boundaries.
+ // Note that the first listener of `asBroadcastStream` determines in which
+ // zone the subscription lives.
+ catchErrors(() {
+ catchErrors(() {
+ controller = new StreamController();
+ stream = controller.stream
+ .map((x) {
+ events.add("map $x");
+ return x + 100;
+ })
+ .transform(new StreamTransformer(
+ handleError: (e, sink) => sink.add("error $e")))
+ .asBroadcastStream();
+ stream.listen((x) { events.add("stream $x"); });
+ }).listen((x) { events.add(x); })
+ .asFuture().then((_) { events.add("inner done"); });
+ stream.listen((x) { events.add("stream2 $x"); });
+ controller.add(1);
+ // Errors are not allowed to traverse boundaries. This error should be
+ // caught by the outer catchErrors.
+ controller.addError(2);
+ controller.close();
+ }).listen((x) { events.add("outer: $x"); },
+ onDone: () {
+ Expect.listEquals(["map 1",
+ "stream 101",
+ "stream2 101",
+ "outer: 2",
+ "inner done",
+ ],
+ events);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors24_test.dart b/tests/lib/async/catch_errors24_test.dart
new file mode 100644
index 0000000..4a5508f
--- /dev/null
+++ b/tests/lib/async/catch_errors24_test.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ StreamController controller;
+ Stream stream;
+ // Test that the first listen on a `asBroadcastStream` determines the
+ // zone the subscription lives in. In this case the outer listen happens first
+ // and the error reaches `handleError`.
+ catchErrors(() {
+ catchErrors(() {
+ controller = new StreamController();
+ stream = controller.stream
+ .map((x) {
+ events.add("map $x");
+ return x + 100;
+ })
+ .transform(new StreamTransformer(
+ handleError: (e, sink) => sink.add("error $e")))
+ .asBroadcastStream();
+ runAsync(() { stream.listen((x) { events.add("stream $x"); }); });
+ }).listen((x) { events.add(x); })
+ .asFuture().then((_) { events.add("inner done"); });
+ stream.listen((x) { events.add("stream2 $x"); });
+ runAsync(() {
+ controller.add(1);
+ // Errors are not allowed to traverse boundaries, but in this case the
+ // first listener of the broadcast stream is in the same error-zone. So
+ // this should work.
+ controller.addError(2);
+ controller.close();
+ });
+ }).listen((x) { events.add("outer: $x"); },
+ onDone: () {
+ Expect.listEquals(["map 1",
+ "stream2 101",
+ "stream 101",
+ "stream2 error 2",
+ "stream error 2",
+ "inner done",
+ ],
+ events);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors25_test.dart b/tests/lib/async/catch_errors25_test.dart
new file mode 100644
index 0000000..162a4a3
--- /dev/null
+++ b/tests/lib/async/catch_errors25_test.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ StreamController controller;
+ // Test multiple subscribers of an asBroadcastStream inside the same
+ // `catchErrors`.
+ catchErrors(() {
+ var stream = new Stream.fromIterable([1, 2]).asBroadcastStream();
+ stream.listen(events.add);
+ stream.listen(events.add);
+ }).listen((x) { events.add("outer: $x"); },
+ onDone: () {
+ Expect.listEquals([1, 1, 2, 2], events);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors26_test.dart b/tests/lib/async/catch_errors26_test.dart
new file mode 100644
index 0000000..d14e1dd
--- /dev/null
+++ b/tests/lib/async/catch_errors26_test.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ StreamController controller;
+ Stream stream;
+ // Test `StreamController.broadcast` streams. Note that the nested listener
+ // doesn't see the error, but the outer one does.
+ catchErrors(() {
+ catchErrors(() {
+ controller = new StreamController.broadcast();
+ controller.stream
+ .map((x) {
+ events.add("map $x");
+ return x + 100;
+ })
+ .transform(new StreamTransformer(
+ handleError: (e, sink) => sink.add("error $e")))
+ .listen((x) { events.add("stream $x"); });
+ }).listen((x) { events.add(x); })
+ .asFuture().then((_) { events.add("inner done"); });
+ controller.stream.listen((x) { events.add("stream2 $x"); },
+ onError: (x) { events.add("stream2 error $x"); });
+ controller.add(1);
+ // Errors are not allowed to traverse boundaries, but in this case the
+ // first listener of the broadcast stream is in the same error-zone. So
+ // this should work.
+ controller.addError(2);
+ controller.close();
+ }).listen((x) { events.add("outer: $x"); },
+ onDone: () {
+ Expect.listEquals(["map 1",
+ "stream 101",
+ "stream2 1",
+ "stream2 error 2",
+ "outer: 2",
+ "inner done",
+ ],
+ events);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors27_test.dart b/tests/lib/async/catch_errors27_test.dart
new file mode 100644
index 0000000..a73c51d
--- /dev/null
+++ b/tests/lib/async/catch_errors27_test.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ StreamController controller;
+ Stream stream;
+ // Test that the first listen on a `asBroadcastStream` determines the
+ // zone the subscription lives in. The inner listen happens first, and
+ // the outer listener must not see the error since it would cross a
+ // zone boundary. It is therefore given to the inner `catchErrors`.
+ catchErrors(() {
+ catchErrors(() {
+ controller = new StreamController();
+ stream = controller.stream
+ .map((x) {
+ events.add("map $x");
+ return x + 100;
+ })
+ .asBroadcastStream();
+ stream
+ .transform(new StreamTransformer(
+ handleError: (e, sink) => sink.add("error $e")))
+ .listen((x) { events.add("stream $x"); });
+ runAsync(() {
+ controller.add(1);
+ // Errors are not allowed to traverse boundaries, but in this case the
+ // first listener of the broadcast stream is in the same error-zone. So
+ // this should work.
+ controller.addError(2);
+ controller.close();
+ });
+ }).listen((x) { events.add(x); })
+ .asFuture().then((_) { events.add("inner done"); });
+ stream.listen((x) { events.add("stream2 $x"); });
+ }).listen((x) { events.add("outer: $x"); },
+ onDone: () {
+ Expect.listEquals(["map 1",
+ "stream 101",
+ "stream2 101",
+ "stream error 2",
+ 2, // Caught by the inner `catchErrors`.
+ "inner done",
+ ],
+ events);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors28_test.dart b/tests/lib/async/catch_errors28_test.dart
new file mode 100644
index 0000000..09a208f
--- /dev/null
+++ b/tests/lib/async/catch_errors28_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Test that periodic Timers are handled correctly by `catchErrors`.
+ catchErrors(() {
+ int counter = 0;
+ new Timer.periodic(const Duration(milliseconds: 50), (timer) {
+ if (counter == 5) timer.cancel();
+ counter++;
+ events.add(counter);
+ });
+ }).listen((x) {
+ events.add(x);
+ },
+ onDone: () {
+ Expect.listEquals([
+ "main exit",
+ 1, 2, 3, 4, 5, 6,
+ ],
+ events);
+ port.close();
+ });
+ events.add("main exit");
+}
diff --git a/tests/lib/async/catch_errors2_test.dart b/tests/lib/async/catch_errors2_test.dart
new file mode 100644
index 0000000..18fefaf
--- /dev/null
+++ b/tests/lib/async/catch_errors2_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ bool futureWasExecuted = false;
+ // Make sure that `catchErrors` only closes the error stream when the inner
+ // futures are done.
+ catchErrors(() {
+ new Future(() {
+ futureWasExecuted = true;
+ });
+ return 'allDone';
+ }).listen((x) {
+ Expect.fail("Unexpected callback");
+ },
+ onDone: () {
+ Expect.isTrue(futureWasExecuted);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors3_test.dart b/tests/lib/async/catch_errors3_test.dart
new file mode 100644
index 0000000..2edcfb2
--- /dev/null
+++ b/tests/lib/async/catch_errors3_test.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ bool futureWasExecuted = false;
+ bool future2WasExecuted = false;
+
+ // Make sure `catchErrors` doesn't close its error stream before all
+ // asynchronous operations are done.
+ catchErrors(() {
+ new Future(() => 499).then((x) {
+ futureWasExecuted = true;
+ });
+ runAsync(() {
+ new Future(() => 42).then((x) {
+ future2WasExecuted = true;
+ });
+ });
+ return 'allDone';
+ }).listen((x) {
+ Expect.fail("Unexpected callback");
+ },
+ onDone: () {
+ Expect.isTrue(futureWasExecuted);
+ Expect.isTrue(future2WasExecuted);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors4_test.dart b/tests/lib/async/catch_errors4_test.dart
new file mode 100644
index 0000000..5c1900b
--- /dev/null
+++ b/tests/lib/async/catch_errors4_test.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Test that synchronous errors inside a `catchErrors` are caught.
+ catchErrors(() {
+ events.add("catch error entry");
+ throw "catch error";
+ }).listen((x) {
+ events.add(x);
+ },
+ onDone: () {
+ Expect.listEquals(["catch error entry",
+ "main exit",
+ "catch error",
+ ],
+ events);
+ port.close();
+ });
+ events.add("main exit");
+}
diff --git a/tests/lib/async/catch_errors5_test.dart b/tests/lib/async/catch_errors5_test.dart
new file mode 100644
index 0000000..1d98f11
--- /dev/null
+++ b/tests/lib/async/catch_errors5_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Test that synchronous *and* asynchronous errors are caught by
+ // `catchErrors`.
+ catchErrors(() {
+ events.add("catch error entry");
+ new Future.error("future error");
+ throw "catch error";
+ }).listen((x) {
+ events.add(x);
+ },
+ onDone: () {
+ Expect.listEquals(["catch error entry",
+ "main exit",
+ "catch error",
+ "future error",
+ ],
+ events);
+ port.close();
+ });
+ events.add("main exit");
+}
diff --git a/tests/lib/async/catch_errors6_test.dart b/tests/lib/async/catch_errors6_test.dart
new file mode 100644
index 0000000..f9b5261
--- /dev/null
+++ b/tests/lib/async/catch_errors6_test.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var completer = new Completer();
+ var errorHandlerOrDoneHasBeenExecuted = false;
+ // Test that `catchErrors` doesn't shut down if a future is never completed.
+ catchErrors(() {
+ completer.future.then((x) { Expect.fail("should not be executed"); });
+ }).listen((x) {
+ errorHandlerOrDoneHasBeenExecuted = true;
+ Expect.fail("should not be executed (listen)");
+ },
+ onDone: () {
+ errorHandlerOrDoneHasBeenExecuted = true;
+ Expect.fail("should not be executed (onDone)");
+ });
+ Timer.run(() {
+ Expect.isFalse(errorHandlerOrDoneHasBeenExecuted);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors7_test.dart b/tests/lib/async/catch_errors7_test.dart
new file mode 100644
index 0000000..1d6310d
--- /dev/null
+++ b/tests/lib/async/catch_errors7_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Test that asynchronous errors are caught.
+ catchErrors(() {
+ events.add("catch error entry");
+ new Future.error("future error");
+ new Future.error("future error2");
+ new Future.value(499).then((x) => throw x);
+ throw "catch error";
+ }).listen((x) {
+ events.add(x);
+ },
+ onDone: () {
+ Expect.listEquals(
+ ["catch error entry",
+ "main exit",
+ "catch error",
+ "future error",
+ "future error2",
+ 499,
+ ],
+ events);
+ port.close();
+ });
+ events.add("main exit");
+}
diff --git a/tests/lib/async/catch_errors8_test.dart b/tests/lib/async/catch_errors8_test.dart
new file mode 100644
index 0000000..f4ce992
--- /dev/null
+++ b/tests/lib/async/catch_errors8_test.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Test nested `catchErrors`.
+ // The nested `catchErrors` throws all kinds of different errors (synchronous
+ // and asynchronous). The body of the outer `catchErrors` furthermore has a
+ // synchronous `throw`.
+ catchErrors(() {
+ events.add("catch error entry");
+ catchErrors(() {
+ events.add("catch error entry2");
+ new Future.error("future error");
+ new Future.error("future error2");
+ new Future.value(499).then((x) => throw x);
+ new Future.delayed(const Duration(milliseconds: 50), () {
+ throw "delayed error";
+ });
+ throw "catch error";
+ }).listen((x) { events.add(x); })
+ .asFuture()
+ .then((_) => events.add("inner done"))
+ .then((_) { throw "inner done throw"; });
+ events.add("after inner");
+ throw "inner throw";
+ }).listen((x) {
+ events.add(x);
+ },
+ onDone: () {
+ Expect.listEquals(["catch error entry",
+ "catch error entry2",
+ "after inner",
+ "main exit",
+ "catch error",
+ "inner throw",
+ "future error",
+ "future error2",
+ 499,
+ "delayed error",
+ "inner done",
+ "inner done throw"
+ ],
+ events);
+ port.close();
+ });
+ events.add("main exit");
+}
diff --git a/tests/lib/async/catch_errors9_test.dart b/tests/lib/async/catch_errors9_test.dart
new file mode 100644
index 0000000..5912bf1
--- /dev/null
+++ b/tests/lib/async/catch_errors9_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ bool futureWasExecuted = false;
+ // Test that `catchErrors` waits for `Timer.run` before closing its error
+ // stream.
+ catchErrors(() {
+ Timer.run(() {
+ futureWasExecuted = true;
+ });
+ return 'allDone';
+ }).listen((x) {
+ Expect.fail("Unexpected callback");
+ },
+ onDone: () {
+ Expect.isTrue(futureWasExecuted);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/catch_errors_test.dart b/tests/lib/async/catch_errors_test.dart
new file mode 100644
index 0000000..c981966
--- /dev/null
+++ b/tests/lib/async/catch_errors_test.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ // Make sure `catchErrors` shuts down the error stream when the synchronous
+ // operation is done and there isn't any asynchronous pending callback.
+ catchErrors(() {
+ return 'allDone';
+ }).listen((x) {
+ Expect.fail("Unexpected callback");
+ },
+ onDone: () {
+ port.close();
+ });
+}
diff --git a/tests/lib/async/run_zoned1_test.dart b/tests/lib/async/run_zoned1_test.dart
new file mode 100644
index 0000000..71af234
--- /dev/null
+++ b/tests/lib/async/run_zoned1_test.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+
+main() {
+ // Make sure `runZoned` returns the result of a synchronous call.
+ Expect.equals(499, runZonedExperimental(() => 499));
+}
diff --git a/tests/lib/async/run_zoned2_test.dart b/tests/lib/async/run_zoned2_test.dart
new file mode 100644
index 0000000..39e28a0
--- /dev/null
+++ b/tests/lib/async/run_zoned2_test.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ // Ensure that `runZoned` is done when a synchronous call finishes.
+ runZonedExperimental(() => 499,
+ onDone: port.close);
+}
diff --git a/tests/lib/async/run_zoned3_test.dart b/tests/lib/async/run_zoned3_test.dart
new file mode 100644
index 0000000..f880208
--- /dev/null
+++ b/tests/lib/async/run_zoned3_test.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ // Ensure that `runZoned` is done when a synchronous call throws.
+ bool sawException = false;
+ try {
+ runZonedExperimental(() { throw 0; },
+ onDone: () {
+ // onDone is executed synchronously.
+ Expect.isFalse(sawException);
+ port.close();
+ });
+ } catch (e) {
+ sawException = true;
+ }
+ Expect.isTrue(sawException);
+}
diff --git a/tests/lib/async/run_zoned4_test.dart b/tests/lib/async/run_zoned4_test.dart
new file mode 100644
index 0000000..ac96b65
--- /dev/null
+++ b/tests/lib/async/run_zoned4_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+
+main() {
+ // Make sure `runZoned` returns the result of a synchronous call when an
+ // error handler is defined.
+ Expect.equals(499,
+ runZonedExperimental(() => 499,
+ onError: (e) { throw "Unexpected"; }));
+}
diff --git a/tests/lib/async/run_zoned5_test.dart b/tests/lib/async/run_zoned5_test.dart
new file mode 100644
index 0000000..bfe7c8b
--- /dev/null
+++ b/tests/lib/async/run_zoned5_test.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ // Ensure that `runZoned`'s onError handles synchronous errors.
+ runZonedExperimental(() { throw 0; },
+ onError: (e) {
+ Expect.equals(0, e);
+ port.close();
+ });
+}
diff --git a/tests/lib/async/run_zoned6_test.dart b/tests/lib/async/run_zoned6_test.dart
new file mode 100644
index 0000000..2de6ca9
--- /dev/null
+++ b/tests/lib/async/run_zoned6_test.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ // Ensure that `runZoned`'s onError handles synchronous errors but delegates
+ // to the top-level when the handler returns false.
+ try {
+ runZonedExperimental(() { throw 0; },
+ onError: (e) {
+ Expect.equals(0, e);
+ port.close();
+ throw e; /// 01: runtime error
+ });
+ } catch (e) {
+ // We should never see an error here.
+ if (true) /// 01: continued
+ rethrow;
+ }
+}
diff --git a/tests/lib/async/run_zoned7_test.dart b/tests/lib/async/run_zoned7_test.dart
new file mode 100644
index 0000000..3bd3dbb
--- /dev/null
+++ b/tests/lib/async/run_zoned7_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Test runZoned with periodic Timers.
+ runZonedExperimental(() {
+ int counter = 0;
+ new Timer.periodic(const Duration(milliseconds: 50), (timer) {
+ if (counter == 5) timer.cancel();
+ counter++;
+ events.add(counter);
+ });
+ }, onDone: () {
+ Expect.listEquals([
+ "main exit",
+ 1, 2, 3, 4, 5, 6,
+ ],
+ events);
+ port.close();
+ });
+ events.add("main exit");
+}
diff --git a/tests/lib/async/run_zoned8_test.dart b/tests/lib/async/run_zoned8_test.dart
new file mode 100644
index 0000000..c8ce77d
--- /dev/null
+++ b/tests/lib/async/run_zoned8_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+import 'catch_errors.dart';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ var events = [];
+ // Test runZoned with periodic Timers.
+ runZonedExperimental(() {
+ int counter = 0;
+ new Timer.periodic(const Duration(milliseconds: 50), (timer) {
+ if (counter == 1) timer.cancel();
+ counter++;
+ events.add(counter);
+ throw counter;
+ });
+ }, onError: (e) {
+ events.add("error: $e");
+ },
+ onDone: () {
+ Expect.listEquals([
+ "main exit",
+ 1, "error: 1", 2, "error: 2",
+ ],
+ events);
+ port.close();
+ });
+ events.add("main exit");
+}
diff --git a/tests/lib/async/run_zoned9_test.dart b/tests/lib/async/run_zoned9_test.dart
new file mode 100644
index 0000000..aeaee5d
--- /dev/null
+++ b/tests/lib/async/run_zoned9_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:isolate';
+
+main() {
+ // We keep a ReceivePort open until all tests are done. This way the VM will
+ // hang if the callbacks are not invoked and the test will time out.
+ var port = new ReceivePort();
+ // Ensure that `runZoned`'s onError handles synchronous errors but delegates
+ // to the next runZonedExperimental when the handler returns false.
+ bool sawInnerHandler = false;
+ try {
+ runZonedExperimental(() {
+ runZonedExperimental(() { throw 0; },
+ onError: (e) {
+ Expect.equals(0, e);
+ sawInnerHandler = true;
+ throw e;
+ });
+ }, onError: (e) {
+ Expect.equals(0, e);
+ Expect.isTrue(sawInnerHandler);
+ port.close();
+ throw e; /// 01: runtime error
+ });
+ } catch (e) {
+ // We should never see an error here.
+ if (true) /// 01: continued
+ rethrow;
+ }
+}
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index ed775e2..e26f707 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -20,19 +20,20 @@
typed_data/byte_data_test: Fail, OK # d8/ie9 doesn't support DataView
[ $compiler == dart2js && $minified ]
+mirrors/disable_tree_shaking_test: Fail # Issue 6490
mirrors/metadata_test: Fail
mirrors/mirrors_resolve_fields_test: Fail # Issue 6490
-mirrors/reflectively_instantiate_uninstantiated_class_test: Fail # Issue 6490
-mirrors/disable_tree_shaking_test: Fail # Issue 6490
mirrors/reflect_model_test: Fail # Issue 6490
+mirrors/reflectively_instantiate_uninstantiated_class_test: Fail # Issue 6490
+mirrors/to_string_test: Fail # Issue 6490
[ $csp ]
-mirrors/library_metadata_test: Fail # Issue 10906
-mirrors/metadata_test: Fail # Issue 10906
+mirrors/metadata_test: Fail # Issue 6490
+mirrors/reflect_model_test: Fail # Issue 6490
mirrors/reflect_runtime_type_test: Fail # Issue 6490
mirrors/reflect_uninstantiated_class_test: Fail # Issue 6490
mirrors/superclass_test: Fail # Issue 6490
-mirrors/reflect_model_test: Fail # Issue 6490
+mirrors/to_string_test: Fail # Issue 6490
[ $compiler == dart2js && $checked ]
async/stream_event_transform_test: Fail # Issue 7733.
@@ -58,6 +59,8 @@
async/stream_periodic3_test: Fail # Timer interface not supported; dartbug.com/7728.
async/stream_periodic4_test: Fail # Timer interface not supported; dartbug.com/7728.
async/stream_periodic5_test: Fail # Timer interface not supported; dartbug.com/7728.
+async/run_zoned7_test: Fail # Timer interface not supported: dartbug.com/7728.
+async/catch_errors22_test: Fail # Timer interface not supported: dartbug.com/7728.
[ $compiler == dart2js && $browser ]
async/timer_not_available_test: Fail, OK # only meant to test when there is no way to
@@ -70,6 +73,7 @@
async/run_async6_test: Fail # Issue 10957 - may be related to issue 10910
mirrors/reflect_model_test: Fail # http://dartbug.com/11219
mirrors/disable_tree_shaking_test: Fail
+mirrors/to_string_test: Fail
[ $compiler == dart2dart && $minified ]
json/json_test: Fail # Issue 10961
diff --git a/tests/lib/mirrors/library_metadata_test.dart b/tests/lib/mirrors/library_metadata_test.dart
index 3ae8b11..259e398 100644
--- a/tests/lib/mirrors/library_metadata_test.dart
+++ b/tests/lib/mirrors/library_metadata_test.dart
@@ -7,39 +7,10 @@
import 'dart:mirrors';
-const string = 'a metadata string';
-
-const symbol = const Symbol('symbol');
-
-const hest = 'hest';
-
-checkMetadata(DeclarationMirror mirror, List expectedMetadata) {
- List metadata = mirror.metadata.map((m) => m.reflectee).toList();
- if (metadata == null) {
- throw 'Null metadata on $mirror';
- }
- int expectedLength = expectedMetadata.length;
- int actualLength = metadata.length;
- if (expectedLength != actualLength) {
- throw 'Expected length = $expectedLength, but got length = $actualLength.';
- }
- for (int i = 0; i < expectedLength; i++) {
- if (metadata[i] != expectedMetadata[i]) {
- throw '${metadata[i]} is not "${expectedMetadata[i]}"'
- ' in $mirror at index $i';
- }
- }
- print(metadata);
-}
+import 'metadata_test.dart';
main() {
- if (MirrorSystem.getName(symbol) != 'symbol') {
- // This happened in dart2js due to how early library metadata is
- // computed.
- throw 'Bad constant: $symbol';
- }
-
MirrorSystem mirrors = currentMirrorSystem();
checkMetadata(mirrors.findLibrary(const Symbol('test.library_metadata_test')).first,
- [string, symbol]);
+ [string, symbol]);
}
diff --git a/tests/lib/mirrors/to_string_test.dart b/tests/lib/mirrors/to_string_test.dart
new file mode 100644
index 0000000..ac58b2a
--- /dev/null
+++ b/tests/lib/mirrors/to_string_test.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 test.to_string_test;
+
+import 'dart:mirrors';
+
+import 'package:expect/expect.dart';
+
+expect(expected, actual) => Expect.stringEquals(expected, '$actual');
+
+class Foo {
+ var field;
+ method() {}
+}
+
+main() {
+ var mirrors = currentMirrorSystem();
+ expect("TypeMirror on 'dynamic'", mirrors.dynamicType);
+ expect("TypeMirror on 'void'", mirrors.voidType);
+ expect("LibraryMirror on 'test.to_string_test'",
+ mirrors.findLibrary(const Symbol('test.to_string_test')).single);
+ expect("InstanceMirror on 1", reflect(1));
+ expect("ClassMirror on 'Foo'", reflectClass(Foo));
+ expect("VariableMirror on 'field'",
+ reflectClass(Foo).variables.values.single);
+ expect("MethodMirror on 'method'", reflectClass(Foo).methods.values.single);
+ String s = reflect(main).toString();
+ Expect.isTrue(s.startsWith("ClosureMirror on '"), s);
+}
diff --git a/tests/standalone/io/directory_list_pause_test.dart b/tests/standalone/io/directory_list_pause_test.dart
new file mode 100644
index 0000000..178f59f
--- /dev/null
+++ b/tests/standalone/io/directory_list_pause_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for 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 "package:expect/expect.dart";
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+
+void testPauseList() {
+ var keepAlive = new ReceivePort();
+ new Directory("").createTemp().then((d) {
+ // Linux reads 2K at a time, so be sure to be >>.
+ int total = 4 * 1024 + 1;
+ for (int i = 0; i < total; i++) {
+ new File("${d.path}/$i").createSync();
+ }
+ bool first = true;
+ var subscription;
+ int count = 0;
+ subscription = d.list().listen((file) {
+ if (first) {
+ first = false;
+ subscription.pause();
+ Timer.run(() {
+ for (int i = 0; i < total; i++) {
+ new File("${d.path}/$i").deleteSync();
+ }
+ subscription.resume();
+ });
+ }
+ count++;
+ }, onDone: () {
+ Expect.notEquals(total, count);
+ keepAlive.close();
+ d.delete().then((ignore) => keepAlive.close());
+ });
+ });
+}
+
+void main() {
+ testPauseList();
+}
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index 9f81106..e08da8e 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -50,11 +50,6 @@
io/directory_non_ascii_test: Pass, Fail
io/process_non_ascii_test: Pass, Fail
-[ $runtime == vm && $mode == debug && $checked ]
-io/http_client_request_test: Pass, Crash # Issue 11278
-io/http_server_response_test: Pass, Crash # Issue 11278
-io/http_content_length_test: Pass, Crash # Issue 11278
-
[ $compiler == none && $runtime == drt ]
typed_data_isolate_test: Skip # This test uses dart:io
io/*: Skip # Don't run tests using dart:io in the browser
@@ -116,6 +111,7 @@
[ $compiler == dart2js ]
+io/test_runner_test: Fail # Import Uri does not work
number_identity_test: Skip # Bigints and int/double diff. not supported.
typed_data_test: Skip # dart:typed_data support needed.
bytedata_test: Skip # dart:typed_data support needed.
@@ -134,12 +130,12 @@
debugger/*: Skip # Do not run standalone vm debugger tests with dart2js.
left_shift_bit_and_op_test: Skip # Integers exceed dart2js precision.
pow_test: Skip # Precision > 53 bits.
-io/test_runner_test: Fail # Library dart:uri removed.
io/skipping_dart2js_compilations_test: Skip # Library dart:uri removed.
http_launch_test: Skip
53bit_overflow_test: Skip
53bit_overflow_literal_test: Skip
+
[ $compiler == dart2js && $jscl ]
assert_test: Fail, OK # Assumes unspecified fields on the AssertionError.
byte_array_test: Fail, OK # ByteArray
@@ -166,11 +162,12 @@
coverage_test: Crash # Issue: 11207
debugger/basic_debugger_test: Crash # Issue: 11207
debugger/closure_debugger_test: Crash # Issue: 11207
-http_launch_test: Crash # Issue: 11207
+http_launch_test: Fail, Crash # Issue: 11207
left_shift_bit_and_op_test: Fail # Issue: 11207
package/package_isolate_test: Pass, Crash # Issue: 11207
out_of_memory_test: Pass, Crash # Issue: 11207 (Pass on Mac)
typed_data_test: Timeout, Crash # Issue: 11207
+typed_data_isolate_test: Pass, Crash # Issue: 11207
io/*: Skip # Skip IO tests for now as they are still quite flaky.
[ $arch == mips ]
diff --git a/tools/VERSION b/tools/VERSION
index 28542e6..9715532 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
MAJOR 0
MINOR 5
-BUILD 19
+BUILD 20
PATCH 0
diff --git a/tools/create_sdk.py b/tools/create_sdk.py
index 59e9795..1ae2fd1 100755
--- a/tools/create_sdk.py
+++ b/tools/create_sdk.py
@@ -24,6 +24,8 @@
# ....include/
# ......dart_api.h
# ......dart_debugger_api.h
+# ......dart_mirrors_api.h
+# ......dart_native_api.h
# ....lib/
# ......_internal/
# ......async/
@@ -174,6 +176,10 @@
join(INCLUDE, 'dart_api.h'))
copyfile(join(HOME, 'runtime', 'include', 'dart_debugger_api.h'),
join(INCLUDE, 'dart_debugger_api.h'))
+ copyfile(join(HOME, 'runtime', 'include', 'dart_mirrors_api.h'),
+ join(INCLUDE, 'dart_mirrors_api.h'))
+ copyfile(join(HOME, 'runtime', 'include', 'dart_native_api.h'),
+ join(INCLUDE, 'dart_native_api.h'))
#
# Create and populate sdk/lib.
diff --git a/tools/gyp/configurations_make.gypi b/tools/gyp/configurations_make.gypi
index ba2445a..eec4dc5 100644
--- a/tools/gyp/configurations_make.gypi
+++ b/tools/gyp/configurations_make.gypi
@@ -90,7 +90,9 @@
},
'Dart_Release': {
- 'cflags': [ '-O3', ],
+ 'cflags': [ '-O3',
+ '-fno-omit-frame-pointer',
+ ],
},
},
},
diff --git a/tools/test.dart b/tools/test.dart
index b1c587a..c2da69a 100755
--- a/tools/test.dart
+++ b/tools/test.dart
@@ -26,6 +26,7 @@
import "dart:async";
import "dart:io";
+import "dart:math" as math;
import "testing/dart/http_server.dart";
import "testing/dart/record_and_replay.dart";
import "testing/dart/test_options.dart";
@@ -161,6 +162,12 @@
// http://code.google.com/p/selenium/wiki/InternetExplorerDriver.
if (conf['runtime'].startsWith('ie')) {
maxBrowserProcesses = 1;
+ } else if (conf['runtime'].startsWith('safari') &&
+ conf['use_browser_controller']) {
+ // FIXME(kustermann/ricow): Remove this once the new browser_controller is
+ // stable.
+ maxBrowserProcesses = math.max(maxProcesses - 3, 1);
+ maxProcesses = math.max(maxProcesses -1, 1);
}
for (String key in selectors.keys) {
diff --git a/tools/testing/dart/test_runner.dart b/tools/testing/dart/test_runner.dart
index e632359..63c8164 100644
--- a/tools/testing/dart/test_runner.dart
+++ b/tools/testing/dart/test_runner.dart
@@ -1649,7 +1649,7 @@
Future<BrowserTestRunner> _getBrowserTestRunner(TestCase test) {
var local_ip = test.configuration['local_ip'];
var runtime = test.configuration['runtime'];
- var num_browsers = test.configuration['tasks'];
+ var num_browsers = _maxBrowserProcesses;
if (_browserTestRunners[runtime] == null) {
var testRunner =
new BrowserTestRunner(local_ip, runtime, num_browsers);
diff --git a/tools/utils.py b/tools/utils.py
index 9ef6c2f..da42666 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -240,7 +240,8 @@
def GetSVNRevision():
p = subprocess.Popen(['svn', 'info'], stdout = subprocess.PIPE,
- stderr = subprocess.STDOUT, shell=IsWindows())
+ stderr = subprocess.STDOUT, shell=IsWindows(),
+ env = {'LC_MESSAGES' : 'en_GB'})
output, not_used = p.communicate()
revision = ParseSvnInfoOutput(output)
if revision: