blob: be590d1550c570c80f320bab4d8780453d434016 [file] [log] [blame] [edit]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for 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 <cstdio>
#include <cstdlib>
#include <cstring>
// TODO(dartbug.com/40579): This requires static linking to either link
// dart.exe or dartaotruntime.exe on Windows.
// The sample currently fails on Windows in AOT mode.
#include "include/dart_api.h"
#include "include/dart_native_api.h"
static bool is_dartaotruntime = true;
bool IsTreeShaken(const char* name, Dart_Handle handle, const char* error) {
// No tree shaking in the JIT runtime.
if (!is_dartaotruntime) return false;
if (Dart_IsApiError(handle)) {
// All tree-shaking related API errors should include the expected name.
if (strstr(error, name) == nullptr) return false;
// Node was tree shaken (e.g., 'Class C not found in library...').
if (strstr(error, " not found in ") != nullptr) return true;
// Constructor was tree shaken.
if (strstr(error, "Dart_New: could not find ") != nullptr) return true;
} else if (Dart_IsUnhandledExceptionError(handle)) {
// TFA replaces operations with a throw in some cases. If obfuscation
// is turned on and the member has no entry point annotation, then its
// name may be obfuscated in the result and so cannot be depended on.
if (strstr(error, "Attempt to execute code removed by ") != nullptr) {
return true;
}
// All other tree-shaking related unhandled exceptions are NSM errors
// that should include the expected name.
if (strstr(error, name) == nullptr) return false;
if (strstr(error, "NoSuchMethodError: ") == nullptr) {
return false;
}
if (strstr(error, "No top-level method '") != nullptr) return true;
if (strstr(error, "No top-level getter '") != nullptr) return true;
if (strstr(error, "No top-level setter '") != nullptr) return true;
if (strstr(error, "No static method '") != nullptr) return true;
if (strstr(error, "No static getter '") != nullptr) return true;
if (strstr(error, "No static setter '") != nullptr) return true;
if (strstr(error, "' has no instance method '") != nullptr) return true;
if (strstr(error, "' has no instance getter '") != nullptr) return true;
if (strstr(error, "' has no instance setter '") != nullptr) return true;
}
// Not an tree shaking-related error.
return false;
}
#if defined(_MSC_VER)
#define FATAL(fmt, ...) \
do { \
fprintf(stderr, "Failed at %s:%d: " fmt "!\n", __FILE__, __LINE__, \
__VA_ARGS__); \
abort(); \
} while (false)
#else
#define FATAL(fmt, ...) \
do { \
fprintf(stderr, "Failed at %s:%d: " fmt "!\n", __FILE__, __LINE__, \
##__VA_ARGS__); \
abort(); \
} while (false)
#endif
#define CHECK(H) \
do { \
fprintf(stderr, "Checking %s...\n", #H); \
Dart_Handle __handle__ = H; \
if (Dart_IsError(__handle__)) { \
const char* message = Dart_GetError(__handle__); \
FATAL("\n%s", message); \
} else { \
fprintf(stderr, " Check passed.\n\n"); \
} \
} while (false)
#define ASSERT_SUBSTRING(needle, haystack) \
do { \
if (strstr(haystack, needle) == nullptr) { \
FATAL("expected '%s' within:\n%s\n", needle, haystack); \
} \
} while (false)
#define FAIL(name, result) \
do { \
fprintf(stderr, "Expect failure to access '%s'\n", name); \
if (!Dart_IsError(result)) { \
FATAL("No error for accessing %s", name); \
} \
const char* error = Dart_GetError(result); \
if (IsTreeShaken(name, result, error)) { \
fprintf(stderr, " Received error due to tree shaking: %s\n\n", error); \
} else if (!Dart_IsApiError(result)) { \
FATAL("Not an API error for accessing %s: %s", name, error); \
} else { \
ASSERT_SUBSTRING(name, error); \
ASSERT_SUBSTRING("ERROR: ", error); \
ASSERT_SUBSTRING("' from native code, it must be annotated.", error); \
fprintf(stderr, " Received expected error: %s\n\n", error); \
} \
} while (false)
// Some invalid accesses are allowed in AOT since we don't retain @pragma
// annotations. Only use this if there's no other way to detect missing
// annotations, e.g., a function with no code signals that the precompiler
// did not preserve the code object because the function is not annotated
// as a "call" entry point.
#define FAIL_UNLESS_PRECOMPILED(name, result) \
do { \
if (!is_dartaotruntime) { \
FAIL(name, result); \
} else { \
CHECK(result); \
} \
} while (false);
#define FAIL_CLOSURIZE_CONSTRUCTOR(name, result) \
do { \
fprintf(stderr, "Expect failure to closurize constructor '%s'\n", name); \
if (!Dart_IsError(result)) { \
FATAL("No error for closurizing %s", name); \
} \
const char* error = Dart_GetError(result); \
if (!Dart_IsUnhandledExceptionError(result)) { \
FATAL("Not an unhandled exception error for closurizing %s: %s", name, \
error); \
} else { \
ASSERT_SUBSTRING(name, error); \
ASSERT_SUBSTRING("No static getter", error); \
fprintf(stderr, " Received expected error: %s\n\n", error); \
} \
} while (false)
#define TEST_FIELDS(target) \
do { \
/* Since the fields start off initialized to a non-null value and then */ \
/* are updated with a null value, check invocation prior to set. */ \
FAIL("fld0", Dart_GetField(target, Dart_NewStringFromCString("fld0"))); \
FAIL("fld0", \
Dart_Invoke(target, Dart_NewStringFromCString("fld0"), 0, nullptr)); \
FAIL("fld0", Dart_SetField(target, Dart_NewStringFromCString("fld0"), \
Dart_Null())); \
CHECK(Dart_GetField(target, Dart_NewStringFromCString("fld1"))); \
CHECK(Dart_Invoke(target, Dart_NewStringFromCString("fld1"), 0, nullptr)); \
CHECK(Dart_SetField(target, Dart_NewStringFromCString("fld1"), \
Dart_Null())); \
CHECK(Dart_GetField(target, Dart_NewStringFromCString("fld2"))); \
CHECK(Dart_Invoke(target, Dart_NewStringFromCString("fld2"), 0, nullptr)); \
if (Dart_IsLibrary(target) || Dart_IsType(target)) { \
/* There are no implicit setters for static fields, so the pragma */ \
/* must be checked; in precompiled mode, that means a false positive. */ \
FAIL_UNLESS_PRECOMPILED( \
"fld2", Dart_SetField(target, Dart_NewStringFromCString("fld2"), \
Dart_Null())); \
} else { \
FAIL("fld2", Dart_SetField(target, Dart_NewStringFromCString("fld2"), \
Dart_Null())); \
} \
FAIL("fld3", Dart_GetField(target, Dart_NewStringFromCString("fld3"))); \
FAIL("fld3", \
Dart_Invoke(target, Dart_NewStringFromCString("fld3"), 0, nullptr)); \
CHECK(Dart_SetField(target, Dart_NewStringFromCString("fld3"), \
Dart_Null())); \
} while (false)
DART_EXPORT void RunTests() {
is_dartaotruntime = Dart_IsPrecompiledRuntime();
Dart_Handle lib = Dart_RootLibrary();
//////// Test class access.
FAIL("C", Dart_GetClass(lib, Dart_NewStringFromCString("C")));
Dart_Handle D_class = Dart_GetClass(lib, Dart_NewStringFromCString("D"));
CHECK(D_class);
Dart_Handle F_class = Dart_GetClass(lib, Dart_NewStringFromCString("F"));
CHECK(F_class);
//////// Test allocation and constructor invocation.
CHECK(Dart_Allocate(D_class));
FAIL("D.", Dart_New(D_class, Dart_Null(), 0, nullptr));
CHECK(Dart_New(D_class, Dart_NewStringFromCString("defined"), 0, nullptr));
Dart_Handle D =
Dart_New(D_class, Dart_NewStringFromCString("fact"), 0, nullptr);
CHECK(D);
//////// Test actions against methods
fprintf(stderr, "\n\nTesting methods with library target\n\n\n");
FAIL("noop", Dart_Invoke(lib, Dart_NewStringFromCString("noop"), 0, nullptr));
FAIL("fn0", Dart_Invoke(lib, Dart_NewStringFromCString("fn0"), 0, nullptr));
CHECK(Dart_Invoke(lib, Dart_NewStringFromCString("fn1"), 0, nullptr));
FAIL("fn1_get",
Dart_Invoke(lib, Dart_NewStringFromCString("fn1_get"), 0, nullptr));
CHECK(Dart_Invoke(lib, Dart_NewStringFromCString("fn1_call"), 0, nullptr));
FAIL("fn0", Dart_GetField(lib, Dart_NewStringFromCString("fn0")));
CHECK(Dart_GetField(lib, Dart_NewStringFromCString("fn1")));
CHECK(Dart_GetField(lib, Dart_NewStringFromCString("fn1_get")));
FAIL("fn1_call", Dart_GetField(lib, Dart_NewStringFromCString("fn1_call")));
fprintf(stderr, "\n\nTesting methods with class target\n\n\n");
FAIL_CLOSURIZE_CONSTRUCTOR(
"defined", Dart_GetField(D_class, Dart_NewStringFromCString("defined")));
FAIL_CLOSURIZE_CONSTRUCTOR(
"fact", Dart_GetField(D_class, Dart_NewStringFromCString("fact")));
FAIL("fn0", Dart_Invoke(D, Dart_NewStringFromCString("fn0"), 0, nullptr));
FAIL("fn2",
Dart_Invoke(D_class, Dart_NewStringFromCString("fn2"), 0, nullptr));
CHECK(Dart_Invoke(D_class, Dart_NewStringFromCString("fn3"), 0, nullptr));
CHECK(
Dart_Invoke(D_class, Dart_NewStringFromCString("fn3_call"), 0, nullptr));
FAIL("fn3_get",
Dart_Invoke(D_class, Dart_NewStringFromCString("fn3_get"), 0, nullptr));
FAIL("fn2", Dart_GetField(D_class, Dart_NewStringFromCString("fn2")));
CHECK(Dart_GetField(D_class, Dart_NewStringFromCString("fn3")));
FAIL("fn3_call",
Dart_GetField(D_class, Dart_NewStringFromCString("fn3_call")));
CHECK(Dart_GetField(D_class, Dart_NewStringFromCString("fn3_get")));
fprintf(stderr, "\n\nTesting methods with instance target\n\n\n");
CHECK(Dart_Invoke(D, Dart_NewStringFromCString("fn1"), 0, nullptr));
FAIL("fn1_get",
Dart_Invoke(D, Dart_NewStringFromCString("fn1_get"), 0, nullptr));
CHECK(Dart_Invoke(D, Dart_NewStringFromCString("fn1_call"), 0, nullptr));
FAIL("fn0", Dart_GetField(D, Dart_NewStringFromCString("fn0")));
CHECK(Dart_GetField(D, Dart_NewStringFromCString("fn1")));
CHECK(Dart_GetField(D, Dart_NewStringFromCString("fn1_get")));
FAIL("fn1_call", Dart_GetField(D, Dart_NewStringFromCString("fn1_call")));
//////// Test actions against fields
fprintf(stderr, "\n\nTesting fields with library target\n\n\n");
TEST_FIELDS(lib);
fprintf(stderr, "\n\nTesting fields with class target\n\n\n");
TEST_FIELDS(F_class);
fprintf(stderr, "\n\nTesting fields with instance target\n\n\n");
TEST_FIELDS(D);
}