293e84cd5c
Needed by irregexp. TEST=ci Change-Id: Ie64ee2869ddbb5bad234aea9b49d140b96fed2be Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/481242 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
295 lines
15 KiB
C++
295 lines
15 KiB
C++
// 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;
|
|
}
|
|
|
|
#define FATAL(fmt, ...) \
|
|
do { \
|
|
fprintf(stderr, "Failed at %s:%d: " fmt "!\n", __FILE__, __LINE__, \
|
|
##__VA_ARGS__); \
|
|
abort(); \
|
|
} while (false)
|
|
|
|
#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)
|
|
|
|
#define TEST_GETTERS(target) \
|
|
do { \
|
|
FAIL("get1", Dart_GetField(target, Dart_NewStringFromCString("get1"))); \
|
|
CHECK(Dart_GetField(target, Dart_NewStringFromCString("get2"))); \
|
|
CHECK(Dart_GetField(target, Dart_NewStringFromCString("get3"))); \
|
|
} while (false)
|
|
|
|
#define TEST_SETTERS(target, value) \
|
|
do { \
|
|
FAIL("set1", \
|
|
Dart_SetField(target, Dart_NewStringFromCString("set1"), value)); \
|
|
CHECK(Dart_SetField(target, Dart_NewStringFromCString("set2"), value)); \
|
|
CHECK(Dart_SetField(target, Dart_NewStringFromCString("set3"), value)); \
|
|
} 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);
|
|
|
|
//////// Test actions against getter and setter functions.
|
|
|
|
fprintf(stderr, "\n\nTesting getters with library target\n\n\n");
|
|
TEST_GETTERS(lib);
|
|
|
|
fprintf(stderr, "\n\nTesting getters with class target\n\n\n");
|
|
TEST_GETTERS(F_class);
|
|
|
|
fprintf(stderr, "\n\nTesting getters with instance target\n\n\n");
|
|
TEST_GETTERS(D);
|
|
|
|
Dart_Handle test_value =
|
|
Dart_GetField(lib, Dart_NewStringFromCString("testValue"));
|
|
CHECK(test_value);
|
|
|
|
fprintf(stderr, "\n\nTesting setters with library target\n\n\n");
|
|
TEST_SETTERS(lib, test_value);
|
|
|
|
fprintf(stderr, "\n\nTesting setters with class target\n\n\n");
|
|
TEST_SETTERS(F_class, test_value);
|
|
|
|
fprintf(stderr, "\n\nTesting setters with instance target\n\n\n");
|
|
TEST_SETTERS(D, test_value);
|
|
}
|