diff --git a/Makefile b/Makefile index 84bd79b00..bb563c435 100644 --- a/Makefile +++ b/Makefile @@ -239,6 +239,7 @@ include test/libc/stdio/test.mk include test/libc/zipos/test.mk include test/libc/release/test.mk include test/libc/test.mk +include test/portcosmo/test.mk include test/net/http/test.mk include test/net/https/test.mk include test/net/finger/test.mk diff --git a/test/portcosmo/constants.c b/test/portcosmo/constants.c new file mode 100644 index 000000000..fb1eb5277 --- /dev/null +++ b/test/portcosmo/constants.c @@ -0,0 +1,51 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "test/portcosmo/constants.h" + +const int TWO = 175; +const int THREE = 201; + +/* the above int values are unknown at compile-time, but + * ifswitch.c and initstruct.c will both compile without + * raising an error when the -fportcosmo flag is passed + * to a gcc patched with third_party/gcc/portcosmo.patch. + * + * what -fportcosmo does is it modifies the AST so that + * the following errors are intercepted and avoided: + * + * - "case is not constant" -- if there is a switch stmt + * that uses the values TWO or THREE in its cases, it + * will be rewritten into an if-else statement so as + * to prevent this "case is not constant" error. + * + * - "initializer element is not constant" - if there is + * global or static struct that is being initialized + * with the values TWO or THREE, temporaries will be + * used to prevent this error, and a small conditional + * or an __attribute__ ((constructor)) will added to + * the code that later fills the correct struct values. + */ diff --git a/test/portcosmo/constants.h b/test/portcosmo/constants.h new file mode 100644 index 000000000..018cab236 --- /dev/null +++ b/test/portcosmo/constants.h @@ -0,0 +1,14 @@ +#ifndef TEST_PORTCOSMO_CONSTANTS_H +#define TEST_PORTCOSMO_CONSTANTS_H + +extern const int TWO; +extern const int THREE; + +#define TWO TWO +#define THREE THREE + +#define __tmpcosmo_TWO -205 +#define __tmpcosmo_THREE -209 + +#endif /* TEST_PORTCOSMO_CONSTANTS_H */ + diff --git a/test/portcosmo/counter_test.c b/test/portcosmo/counter_test.c new file mode 100644 index 000000000..49d02bb81 --- /dev/null +++ b/test/portcosmo/counter_test.c @@ -0,0 +1,124 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/isystem/stdio.h" +#include "libc/isystem/string.h" +#include "libc/testlib/testlib.h" +#include "test/portcosmo/constants.h" + +/* TODO(ahgamut): + * enumerator values have to be known at compile time, + * currently it is not possible to rewrite the AST to + * handle enums with non-constant values. + * + enum circumstances { extenuating = TWO }; + * + */ + +static void arbswitch(int value, const char **res) { + *res = NULL; + switch (value) { + case 1: { + *res = "1"; + break; + } + + case TWO: + *res = "TWO"; + break; + + case THREE: { + *res = "THREE"; + break; + } + + /* + * TODO(ahgamut): check if arbitrary expressions in switch-cases + * are a common thing before updating portcosmo.patch, right now + * the below case will cause a compilation error: + * + case (TWO * THREE): + *res = "TWO * THREE"; + break; + */ + + case 0: + *res = "0"; + } + if (*res == NULL) { + *res = "wut"; + } +} + +TEST(portcosmo, arbitrary_switch) { + const char *r = NULL; + + arbswitch(1, &r); + ASSERT_STREQ(r, "1"); + + arbswitch(TWO, &r); + ASSERT_STREQ(r, "TWO"); + + arbswitch(THREE, &r); + ASSERT_STREQ(r, "THREE"); + + arbswitch(20, &r); + ASSERT_STREQ(r, "wut"); + + arbswitch(0, &r); + ASSERT_STREQ(r, "0"); + + arbswitch(TWO * THREE, &r); + /* ASSERT_STREQ(r, "TWO * THREE"); */ + ASSERT_STREQ(r, "wut"); +} + +struct toy { + int id; + int value; +}; + +TEST(portcosmo, initstruct_WRONG) { + /* this is an example of portcosmo initializing structs + * incorrectly. it can only happen if one of the values + * in the struct initializer before the modified value + * JUST SO happens to be equal to the temporary value we + * have #defined somewhere. */ + static struct toy w1 = {.id = -205, .value = TWO}; + /* here -205 JUST SO happens to be equal to __tmpcosmo_TWO, + * so the struct w1 ends up being initialized with wrong values */ + + /* this is what WRONGLY happens, + * because in portcosmo we are unable + * to determine the exact (row,col) + * location of struct fields */ + ASSERT_EQ(TWO, w1.id); // WRONG! + ASSERT_EQ(-205, w1.value); // WRONG! + + /* this is what SHOULD happen */ + ASSERT_NE(-205, w1.id); // should be ASSERT_EQ + ASSERT_NE(TWO, w1.value); // should be ASSERT_EQ +} diff --git a/test/portcosmo/ifswitch_test.c b/test/portcosmo/ifswitch_test.c new file mode 100644 index 000000000..8fd89de14 --- /dev/null +++ b/test/portcosmo/ifswitch_test.c @@ -0,0 +1,162 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/isystem/stdio.h" +#include "libc/isystem/string.h" +#include "libc/testlib/testlib.h" +#include "test/portcosmo/constants.h" + +#define CASEMACRO(X) case X: + +static void basicswitch(int value, const char **res) { + *res = NULL; + switch (value) { + case 1: { + // might create a variable in this scope + int a = 21; + *res = "1"; + break; + } + +#ifdef TWO + CASEMACRO(TWO) + *res = "TWO"; + // fall-through +#endif + + case THREE: { + int c = 22; + *res = "THREE"; + break; + } + + case -THREE: + *res = "-THREE"; + break; + + case ~TWO: + int d = 111; + *res = "~TWO"; + break; + + case 19 ... 27: + *res = "19-27"; + break; + + case 0: + *res = "0"; + // fall-through + + default: + int z = 12; + *res = "default"; + break; + } + if (*res == NULL) { + *res = "wut"; + } +} + +TEST(portcosmo, ifswitch) { + const char *r = NULL; + + basicswitch(1, &r); + ASSERT_STREQ(r, "1"); + + basicswitch(TWO, &r); + ASSERT_STREQ(r, "THREE"); // because fallthrough + + basicswitch(THREE, &r); + ASSERT_STREQ(r, "THREE"); + + basicswitch(-THREE, &r); + ASSERT_STREQ(r, "-THREE"); + + basicswitch(~TWO, &r); + ASSERT_STREQ(r, "~TWO"); + + basicswitch(20, &r); + ASSERT_STREQ(r, "19-27"); + + basicswitch(0, &r); + ASSERT_STREQ(r, "default"); // because fallthrough + + basicswitch(29, &r); + ASSERT_STREQ(r, "default"); + + basicswitch(999, &r); + ASSERT_STREQ(r, "default"); +} + +/* testing switch without default */ +static void nodefault(int value, const char **res) { + *res = NULL; + switch (value) { + case 1: { + *res = "1"; + break; + } + +#ifdef TWO + CASEMACRO(TWO) + *res = "TWO"; + break; +#endif + + case THREE: { + *res = "THREE"; + break; + } + + case 0: + *res = "0"; + } + if (*res == NULL) { + *res = "wut"; + } +} + +TEST(portcosmo, ifswitch_nodefault) { + const char *r = NULL; + + nodefault(1, &r); + ASSERT_STREQ(r, "1"); + + nodefault(TWO, &r); + ASSERT_STREQ(r, "TWO"); + + nodefault(THREE, &r); + ASSERT_STREQ(r, "THREE"); + + nodefault(0, &r); + ASSERT_STREQ(r, "0"); + + nodefault(29, &r); + ASSERT_STREQ(r, "wut"); + + nodefault(999, &r); + ASSERT_STREQ(r, "wut"); +} diff --git a/test/portcosmo/initstruct_test.c b/test/portcosmo/initstruct_test.c new file mode 100644 index 000000000..5fe65da88 --- /dev/null +++ b/test/portcosmo/initstruct_test.c @@ -0,0 +1,164 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/isystem/stdio.h" +#include "libc/isystem/string.h" +#include "libc/testlib/testlib.h" +#include "test/portcosmo/constants.h" + +struct toy { + int id; + int value; +}; + +struct toyroom { + int id; + int value; + struct toy x1; + struct toy x2; + struct toy x3; + struct toy x4; +}; + +static int v1 = TWO; +static int arr1[] = {TWO, THREE, ~THREE, ~TWO}; +struct toy t0 = {.id = 30, .value = THREE}; +static struct toy t1 = {.id = 31, .value = TWO}; +static struct toy ta[] = {{.id = 1, .value = ~TWO}, + {.id = ~THREE, .value = TWO}, + {.value = TWO, .id = -THREE}, + {.id = THREE, .value = 1}, + {.id = 7, .value = THREE}}; +static struct toyroom r1 = { + .id = 2, + .value = -TWO, + .x1 = {.id = 1, .value = TWO}, + .x2 = {.id = ~TWO, .value = 1}, + .x3 = {.value = TWO, .id = -THREE}, + .x4 = {.id = THREE, .value = ~THREE}, +}; + +TEST(portcosmo, initstruct_global) { + ASSERT_EQ(v1, TWO); + + ASSERT_EQ(arr1[0], TWO); + ASSERT_EQ(arr1[1], THREE); + ASSERT_EQ(arr1[2], ~THREE); + ASSERT_EQ(arr1[3], ~TWO); + + ASSERT_EQ(t1.id, 31); + ASSERT_EQ(t1.value, TWO); + + ASSERT_EQ(ta[0].id, 1); + ASSERT_EQ(ta[0].value, ~TWO); + ASSERT_EQ(ta[1].id, ~THREE); + ASSERT_EQ(ta[1].value, TWO); + ASSERT_EQ(ta[2].id, -THREE); + ASSERT_EQ(ta[2].value, TWO); + ASSERT_EQ(ta[3].id, THREE); + ASSERT_EQ(ta[3].value, 1); + ASSERT_EQ(ta[4].id, 7); + ASSERT_EQ(ta[4].value, THREE); + + ASSERT_EQ(r1.id, 2); + ASSERT_EQ(r1.value, -TWO); + ASSERT_EQ(r1.x1.id, 1); + ASSERT_EQ(r1.x1.value, TWO); + ASSERT_EQ(r1.x2.id, ~TWO); + ASSERT_EQ(r1.x2.value, 1); + ASSERT_EQ(r1.x3.id, -THREE); + ASSERT_EQ(r1.x3.value, TWO); + ASSERT_EQ(r1.x4.id, THREE); + ASSERT_EQ(r1.x4.value, ~THREE); +} + +void init_func() { + static struct toy box[] = { + {.id = 1, .value = 1}, {.id = -THREE, .value = THREE}, + {.id = THREE, .value = 1}, {.id = -TWO, .value = ~TWO}, + {.id = TWO, .value = THREE}, {.id = ~THREE, .value = TWO}, + }; + static struct toyroom r2 = { + .id = 2, + .value = THREE, + .x1 = {.id = 1, .value = TWO}, + .x2 = {.id = ~TWO, .value = 1}, + .x3 = {.id = TWO, .value = ~THREE}, + .x4 = {.id = THREE, .value = -THREE}, + }; + + static struct toy t = {.id = 22, .value = TWO}; + static int v = TWO; + static int vals[] = {1, TWO, -THREE, ~TWO, 1}; + static int count = 0; + + ASSERT_EQ(box[0].id, 1); + ASSERT_EQ(box[0].value, 1); + ASSERT_EQ(box[1].id, -THREE); + ASSERT_EQ(box[1].value, THREE); + ASSERT_EQ(box[2].id, THREE); + ASSERT_EQ(box[2].value, 1); + ASSERT_EQ(box[3].id, -TWO); + ASSERT_EQ(box[3].value, ~TWO); + ASSERT_EQ(box[4].id, TWO); + ASSERT_EQ(box[4].value, THREE); + ASSERT_EQ(box[5].id, ~THREE); + ASSERT_EQ(box[5].value, TWO); + + ASSERT_EQ(r2.id, 2); + ASSERT_EQ(r2.value, THREE); + ASSERT_EQ(r2.x1.id, 1); + ASSERT_EQ(r2.x1.value, TWO); + ASSERT_EQ(r2.x2.id, ~TWO); + ASSERT_EQ(r2.x2.value, 1); + ASSERT_EQ(r2.x3.id, TWO); + ASSERT_EQ(r2.x3.value, ~THREE); + ASSERT_EQ(r2.x4.id, THREE); + ASSERT_EQ(r2.x4.value, -THREE); + + ASSERT_EQ(t.id, 22); + ASSERT_EQ(t.value, TWO + count); + + ASSERT_EQ(vals[0], 1 + 5 * count); + ASSERT_EQ(vals[1], TWO + 5 * count); + ASSERT_EQ(vals[2], -THREE + 5 * count); + ASSERT_EQ(vals[3], ~TWO + 5 * count); + ASSERT_EQ(vals[4], 1 + 5 * count); + + /* when we call this function a second time, + * we can confirm that the values were not + * wrongly initialized twice */ + for (int i = 0; i < 5; ++i) { + vals[i] += 5; + } + t.value += 1; + count += 1; +} + +TEST(portcosmo, initstruct_local) { + init_func(); + init_func(); +} diff --git a/test/portcosmo/test.mk b/test/portcosmo/test.mk new file mode 100644 index 000000000..c5319c571 --- /dev/null +++ b/test/portcosmo/test.mk @@ -0,0 +1,62 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ + +PKGS += TEST_PORTCOSMO + +TEST_PORTCOSMO_SRCS := $(wildcard test/portcosmo/*.c) +TEST_PORTCOSMO_SRCS_TEST = $(filter %_test.c,$(TEST_PORTCOSMO_SRCS)) + +TEST_PORTCOSMO_OBJS = \ + $(TEST_PORTCOSMO_SRCS:%.c=o/$(MODE)/%.o) + +TEST_PORTCOSMO_COMS = \ + $(TEST_PORTCOSMO_SRCS_TEST:%.c=o/$(MODE)/%.com) + +TEST_PORTCOSMO_BINS = \ + $(TEST_PORTCOSMO_COMS) \ + $(TEST_PORTCOSMO_COMS:%=%.dbg) + +TEST_PORTCOSMO_TESTS = \ + $(TEST_PORTCOSMO_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) + +TEST_PORTCOSMO_CHECKS = \ + $(TEST_PORTCOSMO_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) + +TEST_PORTCOSMO_DIRECTDEPS = \ + LIBC_FMT \ + LIBC_INTRIN \ + LIBC_LOG \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_RUNTIME \ + LIBC_STDIO \ + LIBC_STR \ + LIBC_STUBS \ + LIBC_SYSV \ + LIBC_TESTLIB \ + LIBC_X + +TEST_PORTCOSMO_DEPS := \ + $(call uniq,$(foreach x,$(TEST_PORTCOSMO_DIRECTDEPS),$($(x)))) + +o/$(MODE)/test/portcosmo/%.o: \ + CFLAGS += -fportcosmo + +o/$(MODE)/test/portcosmo/portcosmo.pkg: \ + $(TEST_PORTCOSMO_OBJS) \ + $(foreach x,$(TEST_PORTCOSMO_DIRECTDEPS),$($(x)_A).pkg) + +o/$(MODE)/test/portcosmo/%.com.dbg: \ + $(TEST_PORTCOSMO_DEPS) \ + o/$(MODE)/test/portcosmo/%.o \ + o/$(MODE)/test/portcosmo/constants.o \ + o/$(MODE)/test/portcosmo/portcosmo.pkg \ + $(LIBC_TESTMAIN) \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +.PHONY: o/$(MODE)/test/portcosmo +o/$(MODE)/test/portcosmo: \ + $(TEST_PORTCOSMO_BINS) \ + $(TEST_PORTCOSMO_CHECKS) diff --git a/test/test.mk b/test/test.mk index 02f1f4274..63de20d46 100644 --- a/test/test.mk +++ b/test/test.mk @@ -4,5 +4,6 @@ .PHONY: o/$(MODE)/test o/$(MODE)/test: o/$(MODE)/test/dsp \ o/$(MODE)/test/libc \ + o/$(MODE)/test/portcosmo \ o/$(MODE)/test/net \ o/$(MODE)/test/tool