blob: e0293c0ce42d04ecd7c0af97dbec403ed9537378 [file] [log] [blame] [edit]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "crypto/random.h"
#include "dart/runtime/include/dart_api.h"
#include "mojo/dart/embedder/builtin.h"
#include "mojo/dart/embedder/mojo_natives.h"
namespace mojo {
namespace dart {
// Lists the native functions implementing basic functionality in
// the Mojo embedder dart, such as printing, and file I/O.
#define BUILTIN_NATIVE_LIST(V) \
V(Crypto_GetRandomBytes, 1) \
V(Logger_PrintString, 1) \
V(Builtin_ReadSync, 1) \
V(Builtin_EnumerateFiles, 1) \
V(Builtin_LoadScript, 4) \
V(Builtin_AsyncLoadError, 3) \
V(Builtin_DoneLoading, 0)
BUILTIN_NATIVE_LIST(DECLARE_FUNCTION);
static struct NativeEntries {
const char* name;
Dart_NativeFunction function;
int argument_count;
} BuiltinEntries[] = {BUILTIN_NATIVE_LIST(REGISTER_FUNCTION)};
Dart_NativeFunction Builtin::NativeLookup(Dart_Handle name,
int argument_count,
bool* auto_setup_scope) {
const char* function_name = nullptr;
Dart_Handle result = Dart_StringToCString(name, &function_name);
DART_CHECK_VALID(result);
DCHECK(function_name != nullptr);
DCHECK(auto_setup_scope != nullptr);
*auto_setup_scope = true;
size_t num_entries = arraysize(BuiltinEntries);
for (size_t i = 0; i < num_entries; i++) {
const struct NativeEntries& entry = BuiltinEntries[i];
if (!strcmp(function_name, entry.name) &&
(entry.argument_count == argument_count)) {
return entry.function;
}
}
return nullptr;
}
const uint8_t* Builtin::NativeSymbol(Dart_NativeFunction nf) {
size_t num_entries = arraysize(BuiltinEntries);
for (size_t i = 0; i < num_entries; i++) {
const struct NativeEntries& entry = BuiltinEntries[i];
if (entry.function == nf) {
return reinterpret_cast<const uint8_t*>(entry.name);
}
}
return nullptr;
}
// Implementation of native functions which are used for some
// test/debug functionality in standalone dart mode.
void Logger_PrintString(Dart_NativeArguments args) {
intptr_t length = 0;
uint8_t* chars = nullptr;
Dart_Handle str = Dart_GetNativeArgument(args, 0);
Dart_Handle result = Dart_StringToUTF8(str, &chars, &length);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
} else {
// Uses fwrite to support printing NUL bytes.
fwrite(chars, 1, length, stdout);
fputs("\n", stdout);
}
fflush(stdout);
}
void Builtin_ReadSync(Dart_NativeArguments args) {
intptr_t chars_length = 0;
uint8_t* chars = nullptr;
Dart_Handle file_uri = Dart_GetNativeArgument(args, 0);
if (!Dart_IsString(file_uri)) {
Dart_PropagateError(file_uri);
}
intptr_t file_uri_length = 0;
Dart_Handle result = Dart_StringLength(file_uri, &file_uri_length);
result = Dart_StringToUTF8(file_uri, &chars, &chars_length);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
chars[file_uri_length] = '\0';
base::FilePath path(base::FilePath::FromUTF8Unsafe(
std::string(reinterpret_cast<char*>(chars))));
std::string source;
if (ReadFileToString(path, &source)) {
Dart_Handle data =
Dart_NewTypedData(Dart_TypedData_kUint8, source.length());
char* source_chars = const_cast<char*>(source.c_str());
Dart_ListSetAsBytes(data, 0, reinterpret_cast<uint8_t*>(source_chars),
source.length());
Dart_SetReturnValue(args, data);
return;
}
LOG(ERROR) << "Failed to read path: " << chars;
Dart_SetReturnValue(args, Dart_Null());
}
void Builtin_EnumerateFiles(Dart_NativeArguments args) {
intptr_t chars_length = 0;
uint8_t* chars = nullptr;
Dart_Handle path = Dart_GetNativeArgument(args, 0);
if (!Dart_IsString(path)) {
Dart_PropagateError(path);
}
intptr_t path_length = 0;
Dart_Handle result = Dart_StringLength(path, &path_length);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
result = Dart_StringToUTF8(path, &chars, &chars_length);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
chars[path_length] = '\0';
base::FilePath dir_path(base::FilePath::FromUTF8Unsafe(
std::string(reinterpret_cast<char*>(chars))));
std::vector<std::string> entries;
base::FileEnumerator enumerator(dir_path, false, base::FileEnumerator::FILES);
while (true) {
base::FilePath file_path = enumerator.Next();
std::string file_path_string = file_path.AsUTF8Unsafe();
if (file_path_string == "") {
break;
}
entries.push_back(file_path_string);
}
Dart_Handle list = Dart_NewList(entries.size());
for (uintptr_t i = 0; i < entries.size(); i++) {
Dart_Handle entry = Dart_NewStringFromUTF8(
reinterpret_cast<const uint8_t*>(entries[i].data()),
entries[i].length());
Dart_ListSetAt(list, i, entry);
}
Dart_SetReturnValue(args, list);
}
// Callback function, gets called from asynchronous script and library
// reading code when there is an i/o error.
void Builtin_AsyncLoadError(Dart_NativeArguments args) {
// Dart_Handle source_uri = Dart_GetNativeArgument(args, 0);
Dart_Handle library_uri = Dart_GetNativeArgument(args, 1);
Dart_Handle error = Dart_GetNativeArgument(args, 2);
Dart_Handle library = Dart_LookupLibrary(library_uri);
// If a library with the given uri exists, give it a chance to handle
// the error. If the load requests stems from a deferred library load,
// an IO error is not fatal.
if (!Dart_IsError(library)) {
DCHECK(Dart_IsLibrary(library));
Dart_Handle res = Dart_LibraryHandleError(library, error);
if (Dart_IsNull(res)) {
return;
}
}
// The error was not handled above. Propagate an unhandled exception.
error = Dart_NewUnhandledExceptionError(error);
Dart_PropagateError(error);
}
static const uint8_t* SniffForMagicNumber(const uint8_t* text_buffer,
intptr_t* buffer_len,
bool* is_snapshot) {
intptr_t len = sizeof(Builtin::snapshot_magic_number);
for (intptr_t i = 0; i < len; i++) {
if (text_buffer[i] != Builtin::snapshot_magic_number[i]) {
*is_snapshot = false;
return text_buffer;
}
}
*is_snapshot = true;
DCHECK_GT(*buffer_len, len);
*buffer_len -= len;
return text_buffer + len;
}
// Callback function called when the asynchronous load request of a
// script, library or part is complete.
void Builtin_LoadScript(Dart_NativeArguments args) {
Dart_Handle tag_in = Dart_GetNativeArgument(args, 0);
Dart_Handle resolved_script_uri = Dart_GetNativeArgument(args, 1);
Dart_Handle library_uri = Dart_GetNativeArgument(args, 2);
Dart_Handle source_data = Dart_GetNativeArgument(args, 3);
Dart_TypedData_Type type = Dart_GetTypeOfExternalTypedData(source_data);
bool external = type == Dart_TypedData_kUint8;
uint8_t* data = nullptr;
intptr_t num_bytes;
Dart_Handle result = Dart_TypedDataAcquireData(
source_data, &type, reinterpret_cast<void**>(&data), &num_bytes);
if (Dart_IsError(result))
Dart_PropagateError(result);
scoped_ptr<uint8_t[]> buffer_copy;
if (!external) {
// If the buffer is not external, take a copy.
buffer_copy.reset(new uint8_t[num_bytes]);
memmove(buffer_copy.get(), data, num_bytes);
data = buffer_copy.get();
}
Dart_TypedDataReleaseData(source_data);
if (Dart_IsNull(tag_in) && Dart_IsNull(library_uri)) {
// No tag and a null library_uri indicates this is
// a top-level script, check if it is a snapshot or a regular
// script file and load accordingly.
bool is_snapshot = false;
const uint8_t* payload =
SniffForMagicNumber(data, &num_bytes, &is_snapshot);
if (is_snapshot) {
result = Dart_LoadScriptFromSnapshot(payload, num_bytes);
} else {
Dart_Handle source = Dart_NewStringFromUTF8(data, num_bytes);
if (Dart_IsError(source)) {
result = Builtin::NewError("%s is not a valid UTF-8 script",
resolved_script_uri);
} else {
result = Dart_LoadScript(resolved_script_uri, source, 0, 0);
}
}
} else {
int64_t tag = Builtin::GetIntegerValue(tag_in);
Dart_Handle source = Dart_NewStringFromUTF8(data, num_bytes);
if (Dart_IsError(source)) {
result = Builtin::NewError("%s is not a valid UTF-8 script",
resolved_script_uri);
} else {
if (tag == Dart_kImportTag) {
result = Dart_LoadLibrary(resolved_script_uri, source, 0, 0);
} else {
DCHECK_EQ(tag, Dart_kSourceTag);
Dart_Handle library = Dart_LookupLibrary(library_uri);
DART_CHECK_VALID(library);
result = Dart_LoadSource(library, resolved_script_uri, source, 0, 0);
}
}
}
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
}
// Callback function that gets called from dartutils when there are
// no more outstanding load requests.
void Builtin_DoneLoading(Dart_NativeArguments args) {
Dart_Handle res = Dart_FinalizeLoading(true);
if (Dart_IsError(res)) {
Dart_PropagateError(res);
}
}
static bool GetInt64Value(Dart_Handle value_obj, int64_t* value) {
bool valid = Dart_IsInteger(value_obj);
if (valid) {
Dart_Handle result = Dart_IntegerFitsIntoInt64(value_obj, &valid);
if (Dart_IsError(result))
Dart_PropagateError(result);
}
if (!valid)
return false;
Dart_Handle result = Dart_IntegerToInt64(value_obj, value);
if (Dart_IsError(result))
Dart_PropagateError(result);
return true;
}
void Crypto_GetRandomBytes(Dart_NativeArguments args) {
Dart_Handle count_obj = Dart_GetNativeArgument(args, 0);
const int64_t kMaxRandomBytes = 4096;
int64_t count64 = 0;
if (!GetInt64Value(count_obj, &count64) || (count64 < 0) ||
(count64 > kMaxRandomBytes)) {
Dart_Handle error = Dart_NewStringFromCString(
"Invalid argument: count must be a positive int "
"less than or equal to 4096.");
Dart_ThrowException(error);
}
intptr_t count = static_cast<intptr_t>(count64);
scoped_ptr<uint8_t[]> buffer(new uint8_t[count]);
crypto::RandBytes(reinterpret_cast<void*>(buffer.get()), count);
Dart_Handle result = Dart_NewTypedData(Dart_TypedData_kUint8, count);
if (Dart_IsError(result)) {
Dart_Handle error = Dart_NewStringFromCString(
"Failed to allocate storage.");
Dart_ThrowException(error);
}
Dart_ListSetAsBytes(result, 0, buffer.get(), count);
Dart_SetReturnValue(args, result);
}
} // namespace bin
} // namespace dart