// 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 #include #include // 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); }