blob: b0c773eab2c413044e11a8a51a158471dde8f644 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/unit_test.h"
#include <stdio.h>
#include "bin/builtin.h"
#include "bin/dartutils.h"
#include "bin/isolate_data.h"
#include "platform/globals.h"
#include "vm/assembler.h"
#include "vm/ast_printer.h"
#include "vm/compiler.h"
#include "vm/dart_api_impl.h"
#include "vm/disassembler.h"
#include "vm/isolate_reload.h"
#include "vm/parser.h"
#include "vm/symbols.h"
#include "vm/thread.h"
#include "vm/virtual_memory.h"
using dart::bin::Builtin;
using dart::bin::DartUtils;
namespace dart {
TestCaseBase* TestCaseBase::first_ = NULL;
TestCaseBase* TestCaseBase::tail_ = NULL;
TestCaseBase::TestCaseBase(const char* name) : next_(NULL), name_(name) {
if (first_ == NULL) {
first_ = this;
} else {
tail_->next_ = this;
}
tail_ = this;
}
void TestCaseBase::RunAll() {
TestCaseBase* test = first_;
while (test != NULL) {
test->RunTest();
test = test->next_;
}
}
Dart_Isolate TestCase::CreateIsolate(const uint8_t* buffer, const char* name) {
bin::IsolateData* isolate_data = new bin::IsolateData(name, NULL, NULL);
char* err;
Dart_Isolate isolate = Dart_CreateIsolate(
name, NULL, buffer, NULL, isolate_data, &err);
if (isolate == NULL) {
OS::Print("Creation of isolate failed '%s'\n", err);
free(err);
}
EXPECT(isolate != NULL);
return isolate;
}
static const char* kPackageScheme = "package:";
static bool IsPackageSchemeURL(const char* url_name) {
static const intptr_t kPackageSchemeLen = strlen(kPackageScheme);
return (strncmp(url_name, kPackageScheme, kPackageSchemeLen) == 0);
}
static bool IsImportableTestLib(const char* url_name) {
const char* kImportTestLibUri = "test:importable_lib";
static const intptr_t kImportTestLibUriLen = strlen(kImportTestLibUri);
return (strncmp(url_name, kImportTestLibUri, kImportTestLibUriLen) == 0);
}
static Dart_Handle ImportableTestLibSource() {
const char* kScript =
"importedFunc() => 'a';\n"
"importedIntFunc() => 4;\n"
"class ImportedMixin {\n"
" mixinFunc() => 'mixin';\n"
"}\n";
return DartUtils::NewString(kScript);
}
#ifndef PRODUCT
static bool IsIsolateReloadTestLib(const char* url_name) {
const char* kIsolateReloadTestLibUri = "test:isolate_reload_helper";
static const intptr_t kIsolateReloadTestLibUriLen =
strlen(kIsolateReloadTestLibUri);
return (strncmp(url_name,
kIsolateReloadTestLibUri,
kIsolateReloadTestLibUriLen) == 0);
}
static Dart_Handle IsolateReloadTestLibSource() {
// Special library with one function.
return DartUtils::NewString("void reloadTest() native 'Reload_Test';\n");
}
static void ReloadTest(Dart_NativeArguments native_args) {
DART_CHECK_VALID(TestCase::TriggerReload());
}
static Dart_NativeFunction IsolateReloadTestNativeResolver(
Dart_Handle name,
int num_of_arguments,
bool* auto_setup_scope) {
return ReloadTest;
}
#endif // !PRODUCT
static Dart_Handle ResolvePackageUri(const char* uri_chars) {
const int kNumArgs = 1;
Dart_Handle dart_args[kNumArgs];
dart_args[0] = DartUtils::NewString(uri_chars);
return Dart_Invoke(DartUtils::BuiltinLib(),
DartUtils::NewString("_filePathFromUri"),
kNumArgs,
dart_args);
}
static ThreadLocalKey script_reload_key = kUnsetThreadLocalKey;
static Dart_Handle LibraryTagHandler(Dart_LibraryTag tag,
Dart_Handle library,
Dart_Handle url) {
if (tag == Dart_kCanonicalizeUrl) {
Dart_Handle library_url = Dart_LibraryUrl(library);
if (Dart_IsError(library_url)) {
return library_url;
}
return Dart_DefaultCanonicalizeUrl(library_url, url);
}
if (tag == Dart_kScriptTag) {
// Reload request.
ASSERT(script_reload_key != kUnsetThreadLocalKey);
const char* script_source =
reinterpret_cast<const char*>(
OSThread::GetThreadLocal(script_reload_key));
ASSERT(script_source != NULL);
OSThread::SetThreadLocal(script_reload_key, 0);
return Dart_LoadScript(url,
NewString(script_source),
0,
0);
}
if (!Dart_IsLibrary(library)) {
return Dart_NewApiError("not a library");
}
if (!Dart_IsString(url)) {
return Dart_NewApiError("url is not a string");
}
const char* url_chars = NULL;
Dart_Handle result = Dart_StringToCString(url, &url_chars);
if (Dart_IsError(result)) {
return Dart_NewApiError("accessing url characters failed");
}
Dart_Handle library_url = Dart_LibraryUrl(library);
const char* library_url_string = NULL;
result = Dart_StringToCString(library_url, &library_url_string);
if (Dart_IsError(result)) {
return result;
}
bool is_dart_scheme_url = DartUtils::IsDartSchemeURL(url_chars);
bool is_io_library = DartUtils::IsDartIOLibURL(library_url_string);
if (is_dart_scheme_url) {
ASSERT(tag == Dart_kImportTag);
// Handle imports of other built-in libraries present in the SDK.
if (DartUtils::IsDartIOLibURL(url_chars)) {
return Builtin::LoadAndCheckLibrary(Builtin::kIOLibrary);
} else if (DartUtils::IsDartBuiltinLibURL(url_chars)) {
return Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary);
} else {
return DartUtils::NewError("Do not know how to load '%s'", url_chars);
}
}
if (IsImportableTestLib(url_chars)) {
return Dart_LoadLibrary(url, ImportableTestLibSource(), 0, 0);
}
NOT_IN_PRODUCT(
if (IsIsolateReloadTestLib(url_chars)) {
Dart_Handle library =
Dart_LoadLibrary(url, IsolateReloadTestLibSource(), 0, 0);
DART_CHECK_VALID(library);
Dart_SetNativeResolver(library, IsolateReloadTestNativeResolver, 0);
return library;
})
if (is_io_library) {
ASSERT(tag == Dart_kSourceTag);
return Dart_LoadSource(library,
url,
Builtin::PartSource(Builtin::kIOLibrary,
url_chars),
0, 0);
}
if (IsPackageSchemeURL(url_chars)) {
Dart_Handle resolved_uri = ResolvePackageUri(url_chars);
DART_CHECK_VALID(resolved_uri);
url_chars = NULL;
Dart_Handle result = Dart_StringToCString(resolved_uri, &url_chars);
if (Dart_IsError(result)) {
return Dart_NewApiError("accessing url characters failed");
}
}
// Do sync loading since unit_test doesn't support async.
Dart_Handle source = DartUtils::ReadStringFromFile(url_chars);
EXPECT_VALID(source);
if (tag == Dart_kImportTag) {
return Dart_LoadLibrary(url, source, 0, 0);
} else {
ASSERT(tag == Dart_kSourceTag);
return Dart_LoadSource(library, url, source, 0, 0);
}
}
Dart_Handle TestCase::LoadTestScript(const char* script,
Dart_NativeEntryResolver resolver,
const char* lib_url,
bool finalize_classes) {
Dart_Handle url = NewString(lib_url);
Dart_Handle source = NewString(script);
Dart_Handle result = Dart_SetLibraryTagHandler(LibraryTagHandler);
EXPECT_VALID(result);
Dart_Handle lib = Dart_LoadScript(url, source, 0, 0);
DART_CHECK_VALID(lib);
result = Dart_SetNativeResolver(lib, resolver, NULL);
DART_CHECK_VALID(result);
if (finalize_classes) {
result = Dart_FinalizeLoading(false);
DART_CHECK_VALID(result);
}
return lib;
}
#ifndef PRODUCT
void TestCase::SetReloadTestScript(const char* script) {
if (script_reload_key == kUnsetThreadLocalKey) {
script_reload_key = OSThread::CreateThreadLocal();
}
ASSERT(script_reload_key != kUnsetThreadLocalKey);
ASSERT(OSThread::GetThreadLocal(script_reload_key) == 0);
// Store the new script in TLS.
OSThread::SetThreadLocal(script_reload_key, reinterpret_cast<uword>(script));
}
Dart_Handle TestCase::TriggerReload() {
Isolate* isolate = Isolate::Current();
{
TransitionNativeToVM transition(Thread::Current());
isolate->ReloadSources(/* test_mode = */ true);
}
return Dart_FinalizeLoading(false);
}
Dart_Handle TestCase::GetReloadErrorOrRootLibrary() {
Isolate* isolate = Isolate::Current();
if (isolate->reload_context() != NULL) {
// We should only have a reload context hanging around if an error occurred.
ASSERT(isolate->reload_context()->has_error());
// Return a handle to the error.
return Api::NewHandle(Thread::Current(),
isolate->reload_context()->error());
}
return Dart_RootLibrary();
}
Dart_Handle TestCase::ReloadTestScript(const char* script) {
SetReloadTestScript(script);
Dart_Handle result = TriggerReload();
if (Dart_IsError(result)) {
return result;
}
return GetReloadErrorOrRootLibrary();
}
#endif // !PRODUCT
Dart_Handle TestCase::LoadCoreTestScript(const char* script,
Dart_NativeEntryResolver resolver) {
return LoadTestScript(script, resolver, CORELIB_TEST_URI);
}
Dart_Handle TestCase::lib() {
Dart_Handle url = NewString(TestCase::url());
Dart_Handle lib = Dart_LookupLibrary(url);
DART_CHECK_VALID(lib);
ASSERT(Dart_IsLibrary(lib));
return lib;
}
Dart_Handle TestCase::library_handler(Dart_LibraryTag tag,
Dart_Handle library,
Dart_Handle url) {
if (tag == Dart_kCanonicalizeUrl) {
return url;
}
return Api::Success();
}
char* TestCase::BigintToHexValue(Dart_CObject* bigint) {
return bin::CObject::BigintToHexValue(bigint);
}
void AssemblerTest::Assemble() {
const String& function_name = String::ZoneHandle(
Symbols::New(Thread::Current(), name_));
// We make a dummy script so that exception objects can be composed for
// assembler instructions that do runtime calls, in particular on DBC.
const char* kDummyScript = "assembler_test_dummy_function() {}";
const Script& script = Script::Handle(Script::New(
function_name,
String::Handle(String::New(kDummyScript)),
RawScript::kSourceTag));
script.Tokenize(String::Handle());
const Library& lib = Library::Handle(Library::CoreLibrary());
const Class& cls = Class::ZoneHandle(
Class::New(lib,
function_name,
script,
TokenPosition::kMinSource));
Function& function = Function::ZoneHandle(
Function::New(function_name, RawFunction::kRegularFunction,
true, false, false, false, false, cls,
TokenPosition::kMinSource));
code_ = Code::FinalizeCode(function, assembler_);
code_.set_owner(function);
code_.set_exception_handlers(Object::empty_exception_handlers());
if (FLAG_disassemble) {
OS::Print("Code for test '%s' {\n", name_);
const Instructions& instructions =
Instructions::Handle(code_.instructions());
uword start = instructions.EntryPoint();
Disassembler::Disassemble(start, start + assembler_->CodeSize());
OS::Print("}\n");
}
}
CodeGenTest::CodeGenTest(const char* name)
: function_(Function::ZoneHandle()),
node_sequence_(new SequenceNode(TokenPosition::kMinSource,
new LocalScope(NULL, 0, 0))),
default_parameter_values_(new ZoneGrowableArray<const Instance*> ()) {
ASSERT(name != NULL);
const String& function_name = String::ZoneHandle(
Symbols::New(Thread::Current(), name));
// Add function to a class and that class to the class dictionary so that
// frame walking can be used.
Library& lib = Library::Handle(Library::CoreLibrary());
const Class& cls = Class::ZoneHandle(
Class::New(lib, function_name, Script::Handle(),
TokenPosition::kMinSource));
function_ = Function::New(
function_name, RawFunction::kRegularFunction,
true, false, false, false, false, cls, TokenPosition::kMinSource);
function_.set_result_type(Type::Handle(Type::DynamicType()));
const Array& functions = Array::Handle(Array::New(1));
functions.SetAt(0, function_);
cls.SetFunctions(functions);
lib.AddClass(cls);
}
void CodeGenTest::Compile() {
if (function_.HasCode()) return;
ParsedFunction* parsed_function =
new ParsedFunction(Thread::Current(), function_);
parsed_function->SetNodeSequence(node_sequence_);
parsed_function->set_instantiator(NULL);
parsed_function->set_default_parameter_values(default_parameter_values_);
node_sequence_->scope()->AddVariable(
parsed_function->current_context_var());
parsed_function->EnsureExpressionTemp();
node_sequence_->scope()->AddVariable(parsed_function->expression_temp_var());
parsed_function->AllocateVariables();
const Error& error =
Error::Handle(Compiler::CompileParsedFunction(parsed_function));
EXPECT(error.IsNull());
}
bool CompilerTest::TestCompileScript(const Library& library,
const Script& script) {
Isolate* isolate = Isolate::Current();
ASSERT(isolate != NULL);
const Error& error = Error::Handle(Compiler::Compile(library, script));
if (!error.IsNull()) {
OS::Print("Error compiling test script:\n%s\n",
error.ToErrorCString());
}
return error.IsNull();
}
bool CompilerTest::TestCompileFunction(const Function& function) {
Thread* thread = Thread::Current();
ASSERT(thread != NULL);
ASSERT(ClassFinalizer::AllClassesFinalized());
const Error& error = Error::Handle(Compiler::CompileFunction(thread,
function));
return error.IsNull();
}
void ElideJSONSubstring(const char* prefix, const char* in, char* out) {
const char* pos = strstr(in, prefix);
while (pos != NULL) {
// Copy up to pos into the output buffer.
while (in < pos) {
*out++ = *in++;
}
// Skip to the close quote.
in += strcspn(in, "\"");
pos = strstr(in, prefix);
}
// Copy the remainder of in to out.
while (*in != '\0') {
*out++ = *in++;
}
*out = '\0';
}
} // namespace dart