From 633776b92f63dba80eab477c430c109183b87711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 1 Apr 2020 11:28:03 +0200 Subject: [PATCH 01/79] [apps/shared] Fix CurveView::drawArrow on arrow without orientation --- apps/shared/curve_view.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/shared/curve_view.cpp b/apps/shared/curve_view.cpp index fd7f25090..d0142cce0 100644 --- a/apps/shared/curve_view.cpp +++ b/apps/shared/curve_view.cpp @@ -472,6 +472,10 @@ void CurveView::drawArrow(KDContext * ctx, KDRect rect, float x, float y, float * **/ assert(angle >= 0.0f); + if (std::fabs(dx) < FLT_EPSILON && std::fabs(dy) < FLT_EPSILON) { + // We can't draw an arrow without any orientation + return; + } /* We compute the arrow segments in pixels in order to correctly size the * arrow without depending on the displayed range. * Warning: the computed values are relative so we need to add/subtract the From 76e0deb9bffb25610b6bf0f254732ffaba562281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 1 Apr 2020 10:54:26 +0200 Subject: [PATCH 02/79] [tests/solver] Test solve equation with max number of variables --- apps/solver/test/equation_store.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/solver/test/equation_store.cpp b/apps/solver/test/equation_store.cpp index c36817d6c..786d35eeb 100644 --- a/apps/solver/test/equation_store.cpp +++ b/apps/solver/test/equation_store.cpp @@ -165,6 +165,15 @@ QUIZ_CASE(equation_solve) { const char * solutions22[] = {"\u0012\u0012-π-20\u0013/\u00128\u0013\u0013", "\u0012\u0012π+20\u0013/\u00128\u0013\u0013", "\u0012\u0012π\u0013/\u00124\u0013\u0013", "3", "-8", "5"}; // (-π-20)/8, (π+20)/8, π/4, 3, 5, -8 assert_equation_system_exact_solve_to(equations22, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesxyzabc, solutions22, 6); + /* This test case needs the user defined variable. Indeed, in the equation + * store, m_variables is just before m_userVariables, so bad fetching in + * m_variables might fetch into m_userVariables and create problems. */ + assert_simplify("0→x"); + const char * variablesbDeyzt[] = {"b", "D", "e", "y", "z", "t"}; + const char * equations23[] = {"b=0", "D=0", "e=0", "", "x+y+z+t=0", 0}; + assert_equation_system_exact_solve_to(equations23, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesbDeyzt, nullptr, INT_MAX); + Ion::Storage::sharedStorage()->recordNamed("x.exp").destroy(); + // Monovariable non-polynomial equation double solutions15[] = {-90.0, 90.0}; assert_equation_approximate_solve_to("cos(x)=0", -100.0, 100.0, "x", solutions15, 2, false); From 3fac1fdf783e6e994bdae28f756933abe9ac4834 Mon Sep 17 00:00:00 2001 From: Quentin Date: Wed, 1 Apr 2020 12:45:00 +0200 Subject: [PATCH 03/79] [Feature] Stay Home --- apps/home/base.de.i18n | 2 +- apps/home/base.en.i18n | 2 +- apps/home/base.es.i18n | 2 +- apps/home/base.fr.i18n | 2 +- apps/home/base.hu.i18n | 2 +- apps/home/base.pt.i18n | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/home/base.de.i18n b/apps/home/base.de.i18n index 99f705c4b..a28432cae 100644 --- a/apps/home/base.de.i18n +++ b/apps/home/base.de.i18n @@ -1,4 +1,4 @@ Apps = "Anwendungen" -AppsCapital = "OMEGA" +AppsCapital = "STAY HOME(GA)" ForbidenAppInExamMode1 = "Diese Anwendung ist im" ForbidenAppInExamMode2 = "Prüfungsmodus verboten" diff --git a/apps/home/base.en.i18n b/apps/home/base.en.i18n index dde4e2129..fd5f8048b 100644 --- a/apps/home/base.en.i18n +++ b/apps/home/base.en.i18n @@ -1,4 +1,4 @@ Apps = "Applications" -AppsCapital = "OMEGA" +AppsCapital = "STAY HOME(GA)" ForbidenAppInExamMode1 = "This application is" ForbidenAppInExamMode2 = "forbidden in exam mode" diff --git a/apps/home/base.es.i18n b/apps/home/base.es.i18n index 9d3258766..bdd8b7fba 100644 --- a/apps/home/base.es.i18n +++ b/apps/home/base.es.i18n @@ -1,4 +1,4 @@ Apps = "Aplicaciones" -AppsCapital = "OMEGA" +AppsCapital = "STAY HOME(GA)" ForbidenAppInExamMode1 = "This application is" ForbidenAppInExamMode2 = "forbidden in exam mode" diff --git a/apps/home/base.fr.i18n b/apps/home/base.fr.i18n index dde4e2129..fd5f8048b 100644 --- a/apps/home/base.fr.i18n +++ b/apps/home/base.fr.i18n @@ -1,4 +1,4 @@ Apps = "Applications" -AppsCapital = "OMEGA" +AppsCapital = "STAY HOME(GA)" ForbidenAppInExamMode1 = "This application is" ForbidenAppInExamMode2 = "forbidden in exam mode" diff --git a/apps/home/base.hu.i18n b/apps/home/base.hu.i18n index 6b0b18815..67ea70fd9 100644 --- a/apps/home/base.hu.i18n +++ b/apps/home/base.hu.i18n @@ -1,4 +1,4 @@ Apps = "Alkalmazások" -AppsCapital = "OMEGA" +AppsCapital = "STAY HOME(GA)" ForbidenAppInExamMode1 = "Ez az alkalmazás" ForbidenAppInExamMode2 = "tilos vizsga módban" diff --git a/apps/home/base.pt.i18n b/apps/home/base.pt.i18n index 5e84c0ba1..8031958d8 100644 --- a/apps/home/base.pt.i18n +++ b/apps/home/base.pt.i18n @@ -1,4 +1,4 @@ Apps = "Aplicações" -AppsCapital = "OMEGA" +AppsCapital = "STAY HOME(GA)" ForbidenAppInExamMode1 = "This application is" ForbidenAppInExamMode2 = "forbidden in exam mode" From 92c62ce3dc4f7955100b51696ac315f2b3a73533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 1 Apr 2020 11:50:28 +0200 Subject: [PATCH 04/79] [apps/code] Remove superfluous spaces in toolbox --- apps/code/catalog.de.i18n | 2 +- apps/code/catalog.en.i18n | 2 +- apps/code/catalog.es.i18n | 2 +- apps/code/catalog.fr.i18n | 2 +- apps/code/catalog.pt.i18n | 2 +- apps/code/catalog.universal.i18n | 10 +++++----- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index 7fb0c05a5..387ddd3c5 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -139,7 +139,7 @@ PythonRadians = "Convert x from degrees to radians" PythonRandint = "Random integer in [a,b]" PythonRandom = "Floating point number in [0,1[" PythonRandomFunction = "random module function prefix" -PythonRandrange = "Random number in range(start, stop)" +PythonRandrange = "Random number in range(start,stop)" PythonRangeStartStop = "List from start to stop-1" PythonRangeStop = "List from 0 to stop-1" PythonRect = "z in cartesian coordinates" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index 9cea9e6df..36599e4ec 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -139,7 +139,7 @@ PythonRadians = "Convert x from degrees to radians" PythonRandint = "Random integer in [a,b]" PythonRandom = "Floating point number in [0,1[" PythonRandomFunction = "random module function prefix" -PythonRandrange = "Random number in range(start, stop)" +PythonRandrange = "Random number in range(start,stop)" PythonRangeStartStop = "List from start to stop-1" PythonRangeStop = "List from 0 to stop-1" PythonRect = "z in cartesian coordinates" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index 9cea9e6df..36599e4ec 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -139,7 +139,7 @@ PythonRadians = "Convert x from degrees to radians" PythonRandint = "Random integer in [a,b]" PythonRandom = "Floating point number in [0,1[" PythonRandomFunction = "random module function prefix" -PythonRandrange = "Random number in range(start, stop)" +PythonRandrange = "Random number in range(start,stop)" PythonRangeStartStop = "List from start to stop-1" PythonRangeStop = "List from 0 to stop-1" PythonRect = "z in cartesian coordinates" diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n index e13d380bb..5be9abcc9 100644 --- a/apps/code/catalog.fr.i18n +++ b/apps/code/catalog.fr.i18n @@ -139,7 +139,7 @@ PythonRadians = "Conversion de degrés en radians" PythonRandint = "Entier aléatoire dans [a,b]" PythonRandom = "Nombre décimal dans [0,1[" PythonRandomFunction = "Préfixe fonction du module random" -PythonRandrange = "Nombre dans range(start, stop)" +PythonRandrange = "Nombre dans range(start,stop)" PythonRangeStartStop = "Liste de start à stop-1" PythonRangeStop = "Liste de 0 à stop-1" PythonRect = "Conversion en algébrique" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index b0c415ee9..607b635c1 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -139,7 +139,7 @@ PythonRadians = "Convert x from degrees to radians" PythonRandint = "Random integer in [a,b]" PythonRandom = "Floating point number in [0,1[" PythonRandomFunction = "random module function prefix" -PythonRandrange = "Random number in range(start, stop)" +PythonRandrange = "Random number in range(start,stop)" PythonRangeStartStop = "List from start to stop-1" PythonRangeStop = "List from 0 to stop-1" PythonRect = "z in cartesian coordinates" diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n index b01e7042b..64e99af9a 100644 --- a/apps/code/catalog.universal.i18n +++ b/apps/code/catalog.universal.i18n @@ -165,17 +165,17 @@ PythonCommandRandint = "randint(a,b)" PythonCommandRandom = "random()" PythonCommandRandomFunction = "random.function" PythonCommandRandomFunctionWithoutArg = "random.\x11" -PythonCommandRandrange = "randrange(start, stop)" -PythonCommandRangeStartStop = "range(start, stop)" +PythonCommandRandrange = "randrange(start,stop)" +PythonCommandRangeStartStop = "range(start,stop)" PythonCommandRangeStop = "range(stop)" PythonCommandReal = "z.real" PythonCommandRealWithoutArg = ".real" -PythonCommandRect = "rect(r, arg)" +PythonCommandRect = "rect(r,arg)" PythonCommandRemove = "list.remove(x)" PythonCommandRemoveWithoutArg = ".remove(\x11)" PythonCommandReverse = "list.reverse()" PythonCommandReverseWithoutArg = ".reverse()" -PythonCommandRound = "round(x, n)" +PythonCommandRound = "round(x,n)" PythonCommandScatter = "scatter(x,y)" PythonCommandSeed = "seed(x)" PythonCommandSetPixel = "set_pixel(x,y,color)" @@ -227,7 +227,7 @@ PythonTurtleCommandRed = "'red'" PythonTurtleCommandReset = "reset()" PythonTurtleCommandRight = "right(a)" PythonTurtleCommandSetheading = "setheading(a)" -PythonTurtleCommandSetposition = "setposition(x, [y])" +PythonTurtleCommandSetposition = "setposition(x,[y])" PythonTurtleCommandShowturtle = "showturtle()" PythonTurtleCommandSpeed = "speed(x)" PythonTurtleCommandWhite = "'white'" From 11ac25e935742a5b9a77bdcd06f6e0e35d76ac61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 1 Apr 2020 12:11:54 +0200 Subject: [PATCH 05/79] [apps/code] Fix toolbox comment --- apps/code/catalog.de.i18n | 2 +- apps/code/catalog.en.i18n | 2 +- apps/code/catalog.es.i18n | 2 +- apps/code/catalog.pt.i18n | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index 387ddd3c5..81358ca48 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -142,7 +142,7 @@ PythonRandomFunction = "random module function prefix" PythonRandrange = "Random number in range(start,stop)" PythonRangeStartStop = "List from start to stop-1" PythonRangeStop = "List from 0 to stop-1" -PythonRect = "z in cartesian coordinates" +PythonRect = "Convert to cartesian coordinates" PythonRemove = "Remove the first occurrence of x" PythonReverse = "Reverse the elements of the list" PythonRound = "Round to n digits" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index 36599e4ec..2a9d49f08 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -142,7 +142,7 @@ PythonRandomFunction = "random module function prefix" PythonRandrange = "Random number in range(start,stop)" PythonRangeStartStop = "List from start to stop-1" PythonRangeStop = "List from 0 to stop-1" -PythonRect = "z in cartesian coordinates" +PythonRect = "Convert to cartesian coordinates" PythonRemove = "Remove the first occurrence of x" PythonReverse = "Reverse the elements of the list" PythonRound = "Round to n digits" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index 36599e4ec..2a9d49f08 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -142,7 +142,7 @@ PythonRandomFunction = "random module function prefix" PythonRandrange = "Random number in range(start,stop)" PythonRangeStartStop = "List from start to stop-1" PythonRangeStop = "List from 0 to stop-1" -PythonRect = "z in cartesian coordinates" +PythonRect = "Convert to cartesian coordinates" PythonRemove = "Remove the first occurrence of x" PythonReverse = "Reverse the elements of the list" PythonRound = "Round to n digits" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index 607b635c1..9350afcb0 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -142,7 +142,7 @@ PythonRandomFunction = "random module function prefix" PythonRandrange = "Random number in range(start,stop)" PythonRangeStartStop = "List from start to stop-1" PythonRangeStop = "List from 0 to stop-1" -PythonRect = "z in cartesian coordinates" +PythonRect = "Convert to cartesian coordinates" PythonRemove = "Remove the first occurrence of x" PythonReverse = "Reverse the elements of the list" PythonRound = "Round to n digits" From 2f171eb2fdaf3ab86f5c34c2bc8d3a64de33e951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 1 Apr 2020 11:32:21 +0200 Subject: [PATCH 06/79] [python] Add matplotlib test WIP --- python/Makefile | 2 + python/test/execution_environment.cpp | 45 +++++++++++ python/test/execution_environment.h | 15 ++++ python/test/mandelbrot.cpp | 51 +----------- python/test/matplotlib.cpp | 111 ++++++++++++++++++++++++++ 5 files changed, 175 insertions(+), 49 deletions(-) create mode 100644 python/test/execution_environment.cpp create mode 100644 python/test/execution_environment.h create mode 100644 python/test/matplotlib.cpp diff --git a/python/Makefile b/python/Makefile index cb5d5a015..4c0a92323 100644 --- a/python/Makefile +++ b/python/Makefile @@ -189,5 +189,7 @@ $(eval $(call rule_for, \ $(call object_for,$(python_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h tests_src += $(addprefix python/test/,\ + execution_environment.cpp \ mandelbrot.cpp \ + matplotlib.cpp \ ) diff --git a/python/test/execution_environment.cpp b/python/test/execution_environment.cpp new file mode 100644 index 000000000..313176a10 --- /dev/null +++ b/python/test/execution_environment.cpp @@ -0,0 +1,45 @@ +#include "execution_environment.h" +#include +#include +#include + +char TestExecutionEnvironment::s_pythonHeap[TestExecutionEnvironment::s_pythonHeapSize]; + +void TestExecutionEnvironment::printText(const char * text, size_t length) { + quiz_print(text); +} + +// TODO: this will be obsolete when runCode will take a parameter to choose the input type + +void inlineToBeSingleInput(char * buffer, size_t bufferSize, const char * script) { + static const char * openExec = "exec(\""; + static const char * closeExec = "\")"; + assert(strlen(script) + strlen(openExec) + strlen(closeExec) < bufferSize); + char * bufferChar = buffer; + bufferChar += strlcpy(buffer, openExec, bufferSize); + const char * scriptChar = script; + while (*scriptChar != 0) { + assert(bufferChar - buffer + 2 < bufferSize - 1); + if (*scriptChar == '\n') { + // Turn carriage return in {'\', 'n'} to be processed by exec + *bufferChar++ = '\\'; + *bufferChar++ = 'n'; + } else { + *bufferChar++ = *scriptChar; + } + scriptChar++; + } + bufferChar += strlcpy(bufferChar, closeExec, buffer + bufferSize - bufferChar); + assert(bufferChar - buffer < bufferSize); + *bufferChar = 0; +} + +void assert_script_execution_succeeds(const char * script) { + constexpr size_t bufferSize = 500; + char buffer[bufferSize]; + inlineToBeSingleInput(buffer, bufferSize, script); + MicroPython::init(TestExecutionEnvironment::s_pythonHeap, TestExecutionEnvironment::s_pythonHeap + TestExecutionEnvironment::s_pythonHeapSize); + TestExecutionEnvironment env; + env.runCode(buffer); + MicroPython::deinit(); +} diff --git a/python/test/execution_environment.h b/python/test/execution_environment.h new file mode 100644 index 000000000..361e35338 --- /dev/null +++ b/python/test/execution_environment.h @@ -0,0 +1,15 @@ +#include +#include + +class TestExecutionEnvironment : public MicroPython::ExecutionEnvironment { +public: + void printText(const char * text, size_t length) override; + static constexpr int s_pythonHeapSize = Code::App::k_pythonHeapSize; + static char s_pythonHeap[s_pythonHeapSize]; +}; + + +// TODO: this will be obsolete when runCode will take a parameter to choose the input type +void inlineToBeSingleInput(char * buffer, size_t bufferSize, const char * script); + +void assert_script_execution_succeeds(const char * script); diff --git a/python/test/mandelbrot.cpp b/python/test/mandelbrot.cpp index 99e183634..9bc7f088a 100644 --- a/python/test/mandelbrot.cpp +++ b/python/test/mandelbrot.cpp @@ -1,21 +1,5 @@ #include -#include -#include -#include -#include - - -class TestExecutionEnvironment : public MicroPython::ExecutionEnvironment { -public: - void printText(const char * text, size_t length) override; -}; - -void TestExecutionEnvironment::printText(const char * text, size_t length) { - quiz_print(text); -} - -static constexpr int s_pythonHeapSize = Code::App::k_pythonHeapSize; -static char s_pythonHeap[s_pythonHeapSize]; +#include "execution_environment.h" static const char * s_mandelbrotScript = R"(# def mandelbrot(N_iteration): @@ -33,37 +17,6 @@ mandelbrot(2) print('ok') )"; -// TODO: this will be obsolete when runCode will take a parameter to choose the input type - -void inlineToBeSingleInput(char * buffer, size_t bufferSize, const char * script) { - static const char * openExec = "exec(\""; - static const char * closeExec = "\")"; - assert(strlen(script) + strlen(openExec) + strlen(closeExec) < bufferSize); - char * bufferChar = buffer; - bufferChar += strlcpy(buffer, openExec, bufferSize); - const char * scriptChar = script; - while (*scriptChar != 0) { - assert(bufferChar - buffer + 2 < bufferSize - 1); - if (*scriptChar == '\n') { - // Turn carriage return in {'\', 'n'} to be processed by exec - *bufferChar++ = '\\'; - *bufferChar++ = 'n'; - } else { - *bufferChar++ = *scriptChar; - } - scriptChar++; - } - bufferChar += strlcpy(bufferChar, closeExec, buffer + bufferSize - bufferChar); - assert(bufferChar - buffer < bufferSize); - *bufferChar = 0; -} - QUIZ_CASE(python_mandelbrot) { - constexpr size_t bufferSize = 500; - char buffer[bufferSize]; - inlineToBeSingleInput(buffer, bufferSize, s_mandelbrotScript); - MicroPython::init(s_pythonHeap, s_pythonHeap + s_pythonHeapSize); - TestExecutionEnvironment env; - env.runCode(buffer); - MicroPython::deinit(); + assert_script_execution_succeeds(s_mandelbrotScript); } diff --git a/python/test/matplotlib.cpp b/python/test/matplotlib.cpp new file mode 100644 index 000000000..98e55b5e2 --- /dev/null +++ b/python/test/matplotlib.cpp @@ -0,0 +1,111 @@ +#include +#include "execution_environment.h" + +static const char * s_pyplotArrowScript = R"(# +from matplotlib.pyplot import * +arrow(2,3,4,5) +show() +print('ok') +)"; + +static const char * s_pyplotAxisScript = R"(# +from matplotlib.pyplot import * +axis((2,3,4,5)) +axis([2,3,4,5]) +print(axis()) +scatter(0,1) +show() +print('ok') +)"; + +static const char * s_pyplotAxisErrorScript = R"(# +from matplotlib.pyplot import * +axis(2,3,4,5) +print('fail!') +)"; + +static const char * s_pyplotBarScript = R"(# +from matplotlib.pyplot import * +bar([0,2,3],[10,12,23]) +bar([0,2,3],10) +bar([],[]) +bar([1,2,3],[1,2,3],2,3) +bar([1,2,3],[1,2,3],[1,2,3],[1,2,3]) +show() +print('ok') +)"; + +static const char * s_pyplotBarErrorScript = R"(# +from matplotlib.pyplot import * +bar([1,2,3],[1,2,3,4],[1,2,3],[1,2,3]) +show() +print('fail!') +)"; + +static const char * s_pyplotGridScript = R"(# +from matplotlib.pyplot import * +grid(True) +grid() +show() +print('ok') +)"; + +static const char * s_pyplotHistScript = R"(# +from matplotlib.pyplot import * +hist([2,3,4,5,6]) +hist([2,3,4,5,6],23) +hist([2,3,4,5,6],[0,2,3]) +hist([2,3,4,5,6],[0,2,3, 4,5,6,7]) +show() +print('ok') +)"; + +static const char * s_pyplotPlotScript = R"(# +from matplotlib.pyplot import * +plot([2,3,4,5,6]) +plot([2,3,4,5,6],[3,4,5,6,7]) +show() +print('ok') +)"; + +static const char * s_pyplotPlotErrorScript = R"(# +from matplotlib.pyplot import * +plot([2,3,4,5,6],2) +print('Fail!') +)"; + +static const char * s_pyplotScatterScript = R"(# +from matplotlib.pyplot import * +scatter(2,3) +scatter([2,3,4,5,6],[3,4,5,6,7]) +show() +print('ok') +)"; + +static const char * s_pyplotScatterErrorScript = R"(# +from matplotlib.pyplot import * +scatter([2,3,4,5,6],2) +print('Fail!') +)"; + +static const char * s_pyplotTextScript = R"(# +from matplotlib.pyplot import * +text(2,3, "hello") +show() +print('ok') +)"; + +QUIZ_CASE(python_matplotlib_pyplot) { + assert_script_execution_succeeds(s_pyplotArrowScript); + assert_script_execution_succeeds(s_pyplotAxisScript); + assert_script_execution_succeeds(s_pyplotAxisErrorScript); + assert_script_execution_succeeds(s_pyplotBarScript); + assert_script_execution_succeeds(s_pyplotBarErrorScript); + assert_script_execution_succeeds(s_pyplotGridScript); + assert_script_execution_succeeds(s_pyplotHistScript); + assert_script_execution_succeeds(s_pyplotPlotScript); + assert_script_execution_succeeds(s_pyplotPlotErrorScript); + assert_script_execution_succeeds(s_pyplotScatterScript); + assert_script_execution_succeeds(s_pyplotScatterErrorScript); + assert_script_execution_succeeds(s_pyplotTextScript); +} From 2738d30684841250bbb087983ccb20b0f7ae5533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 1 Apr 2020 13:51:13 +0200 Subject: [PATCH 07/79] [python] test: check wether script execution lead to a Python error --- python/port/port.cpp | 5 ++++- python/port/port.h | 2 +- python/test/execution_environment.cpp | 20 +++++++++++++++++--- python/test/execution_environment.h | 10 +++++++++- python/test/mandelbrot.cpp | 1 - python/test/matplotlib.cpp | 24 ++++++------------------ 6 files changed, 37 insertions(+), 25 deletions(-) diff --git a/python/port/port.cpp b/python/port/port.cpp index 77c7b6c4a..be13fbe7a 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -30,7 +30,7 @@ MicroPython::ExecutionEnvironment * MicroPython::ExecutionEnvironment::currentEx return sCurrentExecutionEnvironment; } -void MicroPython::ExecutionEnvironment::runCode(const char * str) { +bool MicroPython::ExecutionEnvironment::runCode(const char * str) { assert(sCurrentExecutionEnvironment == nullptr); sCurrentExecutionEnvironment = this; @@ -38,6 +38,7 @@ void MicroPython::ExecutionEnvironment::runCode(const char * str) { * for the exception handling (because of print). */ mp_hal_set_interrupt_char((int)Ion::Keyboard::Key::Back); + bool runSucceeded = true; nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_lexer_t *lex = mp_lexer_new_from_str_len(0, str, strlen(str), false); @@ -49,6 +50,7 @@ void MicroPython::ExecutionEnvironment::runCode(const char * str) { mp_call_function_0(module_fun); nlr_pop(); } else { // Uncaught exception + runSucceeded = false; /* mp_obj_print_exception is supposed to handle error printing. However, * because we want to print custom information, we copied and modified the * content of mp_obj_print_exception instead of calling it. */ @@ -93,6 +95,7 @@ void MicroPython::ExecutionEnvironment::runCode(const char * str) { assert(sCurrentExecutionEnvironment == this); sCurrentExecutionEnvironment = nullptr; + return runSucceeded; } void MicroPython::ExecutionEnvironment::interrupt() { diff --git a/python/port/port.h b/python/port/port.h index 67eecd3c5..9d14ab3a6 100644 --- a/python/port/port.h +++ b/python/port/port.h @@ -17,7 +17,7 @@ class ExecutionEnvironment { public: ExecutionEnvironment() {} static ExecutionEnvironment * currentExecutionEnvironment(); - void runCode(const char * ); + bool runCode(const char * ); virtual const char * inputText(const char * prompt) { return nullptr; } // Sandbox diff --git a/python/test/execution_environment.cpp b/python/test/execution_environment.cpp index 313176a10..4ba06d292 100644 --- a/python/test/execution_environment.cpp +++ b/python/test/execution_environment.cpp @@ -6,7 +6,9 @@ char TestExecutionEnvironment::s_pythonHeap[TestExecutionEnvironment::s_pythonHeapSize]; void TestExecutionEnvironment::printText(const char * text, size_t length) { - quiz_print(text); + assert(m_printTextIndex + length < k_maxPrintedTextSize); + m_printTextIndex += strlcpy(m_printTextBuffer + m_printTextIndex, text, length + 1); + m_printTextBuffer[m_printTextIndex] = 0; } // TODO: this will be obsolete when runCode will take a parameter to choose the input type @@ -34,12 +36,24 @@ void inlineToBeSingleInput(char * buffer, size_t bufferSize, const char * script *bufferChar = 0; } -void assert_script_execution_succeeds(const char * script) { +bool execute_script(const char * script, const char * outputText = nullptr) { constexpr size_t bufferSize = 500; char buffer[bufferSize]; inlineToBeSingleInput(buffer, bufferSize, script); MicroPython::init(TestExecutionEnvironment::s_pythonHeap, TestExecutionEnvironment::s_pythonHeap + TestExecutionEnvironment::s_pythonHeapSize); TestExecutionEnvironment env; - env.runCode(buffer); + bool executionResult = env.runCode(buffer); MicroPython::deinit(); + if (outputText) { + quiz_assert(strcmp(outputText, env.lastPrintedText()) == 0); + } + return executionResult; +} + +void assert_script_execution_succeeds(const char * script, const char * outputText) { + quiz_assert(execute_script(script, outputText)); +} + +void assert_script_execution_fails(const char * script) { + quiz_assert(!execute_script(script)); } diff --git a/python/test/execution_environment.h b/python/test/execution_environment.h index 361e35338..f77985cb4 100644 --- a/python/test/execution_environment.h +++ b/python/test/execution_environment.h @@ -3,13 +3,21 @@ class TestExecutionEnvironment : public MicroPython::ExecutionEnvironment { public: + TestExecutionEnvironment() : m_printTextIndex(0) {} void printText(const char * text, size_t length) override; + const char * lastPrintedText() const { return m_printTextBuffer; } + static constexpr int s_pythonHeapSize = Code::App::k_pythonHeapSize; static char s_pythonHeap[s_pythonHeapSize]; +private: + static constexpr size_t k_maxPrintedTextSize = 256; + char m_printTextBuffer[k_maxPrintedTextSize]; + size_t m_printTextIndex; }; // TODO: this will be obsolete when runCode will take a parameter to choose the input type void inlineToBeSingleInput(char * buffer, size_t bufferSize, const char * script); -void assert_script_execution_succeeds(const char * script); +void assert_script_execution_succeeds(const char * script, const char * outputText = nullptr); +void assert_script_execution_fails(const char * script); diff --git a/python/test/mandelbrot.cpp b/python/test/mandelbrot.cpp index 9bc7f088a..0c59bbfa8 100644 --- a/python/test/mandelbrot.cpp +++ b/python/test/mandelbrot.cpp @@ -14,7 +14,6 @@ def mandelbrot(N_iteration): rgb = int(255*i/N_iteration) col = (int(rgb),int(rgb*0.75),int(rgb*0.25)) mandelbrot(2) -print('ok') )"; QUIZ_CASE(python_mandelbrot) { diff --git a/python/test/matplotlib.cpp b/python/test/matplotlib.cpp index 98e55b5e2..9b8c05148 100644 --- a/python/test/matplotlib.cpp +++ b/python/test/matplotlib.cpp @@ -5,7 +5,6 @@ static const char * s_pyplotArrowScript = R"(# from matplotlib.pyplot import * arrow(2,3,4,5) show() -print('ok') )"; static const char * s_pyplotAxisScript = R"(# @@ -15,13 +14,11 @@ axis([2,3,4,5]) print(axis()) scatter(0,1) show() -print('ok') )"; static const char * s_pyplotAxisErrorScript = R"(# from matplotlib.pyplot import * axis(2,3,4,5) -print('fail!') )"; static const char * s_pyplotBarScript = R"(# @@ -32,14 +29,12 @@ bar([],[]) bar([1,2,3],[1,2,3],2,3) bar([1,2,3],[1,2,3],[1,2,3],[1,2,3]) show() -print('ok') )"; static const char * s_pyplotBarErrorScript = R"(# from matplotlib.pyplot import * bar([1,2,3],[1,2,3,4],[1,2,3],[1,2,3]) show() -print('fail!') )"; static const char * s_pyplotGridScript = R"(# @@ -47,7 +42,6 @@ from matplotlib.pyplot import * grid(True) grid() show() -print('ok') )"; static const char * s_pyplotHistScript = R"(# @@ -57,7 +51,6 @@ hist([2,3,4,5,6],23) hist([2,3,4,5,6],[0,2,3]) hist([2,3,4,5,6],[0,2,3, 4,5,6,7]) show() -print('ok') )"; static const char * s_pyplotPlotScript = R"(# @@ -65,13 +58,11 @@ from matplotlib.pyplot import * plot([2,3,4,5,6]) plot([2,3,4,5,6],[3,4,5,6,7]) show() -print('ok') )"; static const char * s_pyplotPlotErrorScript = R"(# from matplotlib.pyplot import * plot([2,3,4,5,6],2) -print('Fail!') )"; static const char * s_pyplotScatterScript = R"(# @@ -79,33 +70,30 @@ from matplotlib.pyplot import * scatter(2,3) scatter([2,3,4,5,6],[3,4,5,6,7]) show() -print('ok') )"; static const char * s_pyplotScatterErrorScript = R"(# from matplotlib.pyplot import * scatter([2,3,4,5,6],2) -print('Fail!') )"; static const char * s_pyplotTextScript = R"(# from matplotlib.pyplot import * -text(2,3, "hello") +text(2,3,'hello') show() -print('ok') )"; QUIZ_CASE(python_matplotlib_pyplot) { assert_script_execution_succeeds(s_pyplotArrowScript); - assert_script_execution_succeeds(s_pyplotAxisScript); - assert_script_execution_succeeds(s_pyplotAxisErrorScript); + assert_script_execution_succeeds(s_pyplotAxisScript, "(2.0, 3.0, 4.0, 5.0)\n"); + assert_script_execution_fails(s_pyplotAxisErrorScript); assert_script_execution_succeeds(s_pyplotBarScript); - assert_script_execution_succeeds(s_pyplotBarErrorScript); + assert_script_execution_fails(s_pyplotBarErrorScript); assert_script_execution_succeeds(s_pyplotGridScript); assert_script_execution_succeeds(s_pyplotHistScript); assert_script_execution_succeeds(s_pyplotPlotScript); - assert_script_execution_succeeds(s_pyplotPlotErrorScript); + assert_script_execution_fails(s_pyplotPlotErrorScript); assert_script_execution_succeeds(s_pyplotScatterScript); - assert_script_execution_succeeds(s_pyplotScatterErrorScript); + assert_script_execution_fails(s_pyplotScatterErrorScript); assert_script_execution_succeeds(s_pyplotTextScript); } From d7804ad5d1a6989add4a9f24b496b9bdf9271c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 1 Apr 2020 13:57:11 +0200 Subject: [PATCH 08/79] [python] Tidy matplotlib tests --- python/test/matplotlib.cpp | 45 ++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/python/test/matplotlib.cpp b/python/test/matplotlib.cpp index 9b8c05148..1c68ba6df 100644 --- a/python/test/matplotlib.cpp +++ b/python/test/matplotlib.cpp @@ -7,6 +7,10 @@ arrow(2,3,4,5) show() )"; +QUIZ_CASE(python_matplotlib_pyplot_arrow) { + assert_script_execution_succeeds(s_pyplotArrowScript); +} + static const char * s_pyplotAxisScript = R"(# from matplotlib.pyplot import * axis((2,3,4,5)) @@ -21,6 +25,11 @@ from matplotlib.pyplot import * axis(2,3,4,5) )"; +QUIZ_CASE(python_matplotlib_pyplot_axis) { + assert_script_execution_succeeds(s_pyplotAxisScript, "(2.0, 3.0, 4.0, 5.0)\n"); + assert_script_execution_fails(s_pyplotAxisErrorScript); +} + static const char * s_pyplotBarScript = R"(# from matplotlib.pyplot import * bar([0,2,3],[10,12,23]) @@ -37,6 +46,11 @@ bar([1,2,3],[1,2,3,4],[1,2,3],[1,2,3]) show() )"; +QUIZ_CASE(python_matplotlib_pyplot_bar) { + assert_script_execution_succeeds(s_pyplotBarScript); + assert_script_execution_fails(s_pyplotBarErrorScript); +} + static const char * s_pyplotGridScript = R"(# from matplotlib.pyplot import * grid(True) @@ -44,6 +58,10 @@ grid() show() )"; +QUIZ_CASE(python_matplotlib_pyplot_grid) { + assert_script_execution_succeeds(s_pyplotGridScript); +} + static const char * s_pyplotHistScript = R"(# from matplotlib.pyplot import * hist([2,3,4,5,6]) @@ -53,6 +71,10 @@ hist([2,3,4,5,6],[0,2,3, 4,5,6,7]) show() )"; +QUIZ_CASE(python_matplotlib_pyplot_hist) { + assert_script_execution_succeeds(s_pyplotHistScript); +} + static const char * s_pyplotPlotScript = R"(# from matplotlib.pyplot import * plot([2,3,4,5,6]) @@ -65,6 +87,11 @@ from matplotlib.pyplot import * plot([2,3,4,5,6],2) )"; +QUIZ_CASE(python_matplotlib_pyplot_plot) { + assert_script_execution_succeeds(s_pyplotPlotScript); + assert_script_execution_fails(s_pyplotPlotErrorScript); +} + static const char * s_pyplotScatterScript = R"(# from matplotlib.pyplot import * scatter(2,3) @@ -77,23 +104,17 @@ from matplotlib.pyplot import * scatter([2,3,4,5,6],2) )"; +QUIZ_CASE(python_matplotlib_pyplot_scatter) { + assert_script_execution_succeeds(s_pyplotScatterScript); + assert_script_execution_fails(s_pyplotScatterErrorScript); +} + static const char * s_pyplotTextScript = R"(# from matplotlib.pyplot import * text(2,3,'hello') show() )"; -QUIZ_CASE(python_matplotlib_pyplot) { - assert_script_execution_succeeds(s_pyplotArrowScript); - assert_script_execution_succeeds(s_pyplotAxisScript, "(2.0, 3.0, 4.0, 5.0)\n"); - assert_script_execution_fails(s_pyplotAxisErrorScript); - assert_script_execution_succeeds(s_pyplotBarScript); - assert_script_execution_fails(s_pyplotBarErrorScript); - assert_script_execution_succeeds(s_pyplotGridScript); - assert_script_execution_succeeds(s_pyplotHistScript); - assert_script_execution_succeeds(s_pyplotPlotScript); - assert_script_execution_fails(s_pyplotPlotErrorScript); - assert_script_execution_succeeds(s_pyplotScatterScript); - assert_script_execution_fails(s_pyplotScatterErrorScript); +QUIZ_CASE(python_matplotlib_pyplot_text) { assert_script_execution_succeeds(s_pyplotTextScript); } From 11dc259566d109e8495e73bf694021576e315d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 1 Apr 2020 14:15:57 +0200 Subject: [PATCH 09/79] [python] test: add a function to run single command test --- python/test/execution_environment.cpp | 17 ++++++++++++----- python/test/execution_environment.h | 1 + 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/python/test/execution_environment.cpp b/python/test/execution_environment.cpp index 4ba06d292..c14781119 100644 --- a/python/test/execution_environment.cpp +++ b/python/test/execution_environment.cpp @@ -36,13 +36,16 @@ void inlineToBeSingleInput(char * buffer, size_t bufferSize, const char * script *bufferChar = 0; } -bool execute_script(const char * script, const char * outputText = nullptr) { +bool execute_input(const char * input, bool singleCommandLine, const char * outputText = nullptr) { constexpr size_t bufferSize = 500; char buffer[bufferSize]; - inlineToBeSingleInput(buffer, bufferSize, script); + if (!singleCommandLine) { + inlineToBeSingleInput(buffer, bufferSize, input); + input = buffer; + } MicroPython::init(TestExecutionEnvironment::s_pythonHeap, TestExecutionEnvironment::s_pythonHeap + TestExecutionEnvironment::s_pythonHeapSize); TestExecutionEnvironment env; - bool executionResult = env.runCode(buffer); + bool executionResult = env.runCode(input); MicroPython::deinit(); if (outputText) { quiz_assert(strcmp(outputText, env.lastPrintedText()) == 0); @@ -51,9 +54,13 @@ bool execute_script(const char * script, const char * outputText = nullptr) { } void assert_script_execution_succeeds(const char * script, const char * outputText) { - quiz_assert(execute_script(script, outputText)); + quiz_assert(execute_input(script, false, outputText)); } void assert_script_execution_fails(const char * script) { - quiz_assert(!execute_script(script)); + quiz_assert(!execute_input(script, false)); +} + +void assert_command_execution_succeeds(const char * line, const char * outputText) { + quiz_assert(execute_input(line, true, outputText)); } diff --git a/python/test/execution_environment.h b/python/test/execution_environment.h index f77985cb4..6e52d5c4b 100644 --- a/python/test/execution_environment.h +++ b/python/test/execution_environment.h @@ -21,3 +21,4 @@ void inlineToBeSingleInput(char * buffer, size_t bufferSize, const char * script void assert_script_execution_succeeds(const char * script, const char * outputText = nullptr); void assert_script_execution_fails(const char * script); +void assert_command_execution_succeeds(const char * line, const char * outputText = nullptr); From ffb8f8f953d835e59434515eeec6a24deefd28e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 1 Apr 2020 14:20:41 +0200 Subject: [PATCH 10/79] [python] Test: add all template scripts --- apps/Makefile | 2 +- apps/code/Makefile | 6 +++++- python/Makefile | 2 +- python/test/basics.cpp | 13 +++++++++++++ python/test/mandelbrot.cpp | 21 --------------------- 5 files changed, 20 insertions(+), 24 deletions(-) create mode 100644 python/test/basics.cpp delete mode 100644 python/test/mandelbrot.cpp diff --git a/apps/Makefile b/apps/Makefile index c3af54608..dc9e4c63e 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -94,7 +94,7 @@ all_app_src = $(app_src)(apps_launch_on_boarding_src) $(apps_launch_default_src) $(call object_for,$(all_app_src)): $(BUILD_DIR)/apps/i18n.h $(call object_for,$(all_app_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h -apps_tests_src = $(app_calculation_test_src) $(app_probability_test_src) $(app_regression_test_src) $(app_sequence_test_src) $(app_shared_test_src) $(app_statistics_test_src) $(app_settings_test_src) $(app_solver_test_src) +apps_tests_src = $(app_calculation_test_src) $(app_code_test_src) $(app_probability_test_src) $(app_regression_test_src) $(app_sequence_test_src) $(app_shared_test_src) $(app_statistics_test_src) $(app_settings_test_src) $(app_solver_test_src) apps_tests_src += $(addprefix apps/,\ global_preferences.cpp \ diff --git a/apps/code/Makefile b/apps/code/Makefile index f05354dff..33b60f68b 100644 --- a/apps/code/Makefile +++ b/apps/code/Makefile @@ -1,6 +1,10 @@ apps += Code::App app_headers += apps/code/app.h +app_code_test_src = $(addprefix apps/code/,\ + script_template.cpp \ +) + app_code_src = $(addprefix apps/code/,\ app.cpp \ console_controller.cpp \ @@ -19,10 +23,10 @@ app_code_src = $(addprefix apps/code/,\ script_node_cell.cpp \ script_parameter_controller.cpp \ script_store.cpp \ - script_template.cpp \ variable_box_controller.cpp \ ) +app_code_src += $(app_code_test_src) app_src += $(app_code_src) i18n_files += $(addprefix apps/code/,\ diff --git a/python/Makefile b/python/Makefile index 4c0a92323..cf3ed9764 100644 --- a/python/Makefile +++ b/python/Makefile @@ -189,7 +189,7 @@ $(eval $(call rule_for, \ $(call object_for,$(python_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h tests_src += $(addprefix python/test/,\ + basics.cpp \ execution_environment.cpp \ - mandelbrot.cpp \ matplotlib.cpp \ ) diff --git a/python/test/basics.cpp b/python/test/basics.cpp new file mode 100644 index 000000000..937b0023f --- /dev/null +++ b/python/test/basics.cpp @@ -0,0 +1,13 @@ +#include +#include "execution_environment.h" + +QUIZ_CASE(python_basics) { + assert_command_execution_succeeds("2+3","5\n"); +} + +QUIZ_CASE(python_template) { + assert_script_execution_succeeds(Code::ScriptTemplate::Squares()->content()); + assert_script_execution_succeeds(Code::ScriptTemplate::Mandelbrot()->content()); + assert_script_execution_succeeds(Code::ScriptTemplate::Polynomial()->content()); + assert_script_execution_succeeds(Code::ScriptTemplate::Parabola()->content()); +} diff --git a/python/test/mandelbrot.cpp b/python/test/mandelbrot.cpp deleted file mode 100644 index 0c59bbfa8..000000000 --- a/python/test/mandelbrot.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include "execution_environment.h" - -static const char * s_mandelbrotScript = R"(# -def mandelbrot(N_iteration): - for x in range(320): - for y in range(222): - z = complex(0,0) - c = complex(3.5*x/319-2.5, -2.5*y/221+1.25) - i = 0 - while (i < N_iteration) and abs(z) < 2: - i = i + 1 - z = z*z+c - rgb = int(255*i/N_iteration) - col = (int(rgb),int(rgb*0.75),int(rgb*0.25)) -mandelbrot(2) -)"; - -QUIZ_CASE(python_mandelbrot) { - assert_script_execution_succeeds(s_mandelbrotScript); -} From d0a77bda2b7c0bfca2210420f507bcd7aff33e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 1 Apr 2020 14:57:25 +0200 Subject: [PATCH 11/79] [python] Improve test API --- python/test/basics.cpp | 4 +- python/test/execution_environment.cpp | 38 ++++-- python/test/execution_environment.h | 8 +- python/test/matplotlib.cpp | 179 +++++++++++++------------- 4 files changed, 122 insertions(+), 107 deletions(-) diff --git a/python/test/basics.cpp b/python/test/basics.cpp index 937b0023f..296b519af 100644 --- a/python/test/basics.cpp +++ b/python/test/basics.cpp @@ -2,7 +2,9 @@ #include "execution_environment.h" QUIZ_CASE(python_basics) { - assert_command_execution_succeeds("2+3","5\n"); + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "2+3","5\n"); + deinit_environment(); } QUIZ_CASE(python_template) { diff --git a/python/test/execution_environment.cpp b/python/test/execution_environment.cpp index c14781119..d606695c6 100644 --- a/python/test/execution_environment.cpp +++ b/python/test/execution_environment.cpp @@ -36,31 +36,51 @@ void inlineToBeSingleInput(char * buffer, size_t bufferSize, const char * script *bufferChar = 0; } -bool execute_input(const char * input, bool singleCommandLine, const char * outputText = nullptr) { - constexpr size_t bufferSize = 500; +bool execute_input(TestExecutionEnvironment env, bool singleCommandLine, const char * input, const char * outputText = nullptr) { + constexpr size_t bufferSize = 1000; char buffer[bufferSize]; if (!singleCommandLine) { inlineToBeSingleInput(buffer, bufferSize, input); input = buffer; } - MicroPython::init(TestExecutionEnvironment::s_pythonHeap, TestExecutionEnvironment::s_pythonHeap + TestExecutionEnvironment::s_pythonHeapSize); - TestExecutionEnvironment env; bool executionResult = env.runCode(input); - MicroPython::deinit(); if (outputText) { quiz_assert(strcmp(outputText, env.lastPrintedText()) == 0); } return executionResult; } +TestExecutionEnvironment init_environement() { + MicroPython::init(TestExecutionEnvironment::s_pythonHeap, TestExecutionEnvironment::s_pythonHeap + TestExecutionEnvironment::s_pythonHeapSize); + return TestExecutionEnvironment(); +} + +void deinit_environment() { + MicroPython::deinit(); +} + +void assert_script_execution_result(bool expectedResult, const char * script, const char * outputText = nullptr) { + TestExecutionEnvironment env = init_environement(); + quiz_assert(expectedResult == execute_input(env, false, script, outputText)); + deinit_environment(); +} + void assert_script_execution_succeeds(const char * script, const char * outputText) { - quiz_assert(execute_input(script, false, outputText)); + assert_script_execution_result(true, script, outputText); } void assert_script_execution_fails(const char * script) { - quiz_assert(!execute_input(script, false)); + assert_script_execution_result(false, script); } -void assert_command_execution_succeeds(const char * line, const char * outputText) { - quiz_assert(execute_input(line, true, outputText)); +void assert_command_execution_result(bool expectedResult, TestExecutionEnvironment env, const char * line, const char * outputText = nullptr) { + quiz_assert(execute_input(env, true,line, outputText) == expectedResult); +} + +void assert_command_execution_succeeds(TestExecutionEnvironment env, const char * line, const char * outputText) { + assert_command_execution_result(true, env, line, outputText); +} + +void assert_command_execution_fails(TestExecutionEnvironment env, const char * line) { + assert_command_execution_result(false, env, line); } diff --git a/python/test/execution_environment.h b/python/test/execution_environment.h index 6e52d5c4b..cc711ad60 100644 --- a/python/test/execution_environment.h +++ b/python/test/execution_environment.h @@ -15,10 +15,10 @@ private: size_t m_printTextIndex; }; - -// TODO: this will be obsolete when runCode will take a parameter to choose the input type -void inlineToBeSingleInput(char * buffer, size_t bufferSize, const char * script); +TestExecutionEnvironment init_environement(); +void deinit_environment(); void assert_script_execution_succeeds(const char * script, const char * outputText = nullptr); void assert_script_execution_fails(const char * script); -void assert_command_execution_succeeds(const char * line, const char * outputText = nullptr); +void assert_command_execution_succeeds(TestExecutionEnvironment env, const char * line, const char * outputText = nullptr); +void assert_command_execution_fails(TestExecutionEnvironment env, const char * line); diff --git a/python/test/matplotlib.cpp b/python/test/matplotlib.cpp index 1c68ba6df..b673d63ca 100644 --- a/python/test/matplotlib.cpp +++ b/python/test/matplotlib.cpp @@ -1,120 +1,113 @@ #include #include "execution_environment.h" -static const char * s_pyplotArrowScript = R"(# -from matplotlib.pyplot import * -arrow(2,3,4,5) -show() -)"; +QUIZ_CASE(python_matplotlib_pyplot_import) { + // Test "from matplotlib.pyplot import *" + TestExecutionEnvironment env = init_environement(); + assert_command_execution_fails(env, "arrow(2,3,4,5)"); + assert_command_execution_succeeds(env, "from matplotlib.pyplot import *"); + assert_command_execution_succeeds(env, "arrow(2,3,4,5)"); + deinit_environment(); + + // "from matplotlib import *" + env = init_environement(); + assert_command_execution_fails(env, "pyplot.arrow(2,3,4,5)"); + assert_command_execution_succeeds(env, "from matplotlib import *"); + assert_command_execution_succeeds(env, "pyplot.arrow(2,3,4,5)"); + deinit_environment(); + + // "import matplotlib" + env = init_environement(); + assert_command_execution_fails(env, "matplotlib.pyplot.arrow(2,3,4,5)"); + assert_command_execution_succeeds(env, "import matplotlib"); + assert_command_execution_succeeds(env, "matplotlib.pyplot.arrow(2,3,4,5)"); + deinit_environment(); + + // "import matplotlib.pyplot" + env = init_environement(); + assert_command_execution_fails(env, "matplotlib.pyplot.arrow(2,3,4,5)"); + assert_command_execution_succeeds(env, "import matplotlib.pyplot"); + assert_command_execution_succeeds(env, "matplotlib.pyplot.arrow(2,3,4,5)"); + deinit_environment(); +} QUIZ_CASE(python_matplotlib_pyplot_arrow) { - assert_script_execution_succeeds(s_pyplotArrowScript); + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from matplotlib.pyplot import *"); + assert_command_execution_succeeds(env, "arrow(2,3,4,5)"); + assert_command_execution_succeeds(env, "show()"); + deinit_environment(); } -static const char * s_pyplotAxisScript = R"(# -from matplotlib.pyplot import * -axis((2,3,4,5)) -axis([2,3,4,5]) -print(axis()) -scatter(0,1) -show() -)"; - -static const char * s_pyplotAxisErrorScript = R"(# -from matplotlib.pyplot import * -axis(2,3,4,5) -)"; - QUIZ_CASE(python_matplotlib_pyplot_axis) { - assert_script_execution_succeeds(s_pyplotAxisScript, "(2.0, 3.0, 4.0, 5.0)\n"); - assert_script_execution_fails(s_pyplotAxisErrorScript); + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from matplotlib.pyplot import *"); + assert_command_execution_succeeds(env, "axis((2,3,4,5))"); + assert_command_execution_succeeds(env, "axis([2,3,4,5])"); + assert_command_execution_succeeds(env, "axis()","(2.0, 3.0, 4.0, 5.0)\n"); + assert_command_execution_succeeds(env, "show()"); + assert_command_execution_fails(env, "axis(2,3,4,5)"); + deinit_environment(); + } -static const char * s_pyplotBarScript = R"(# -from matplotlib.pyplot import * -bar([0,2,3],[10,12,23]) -bar([0,2,3],10) -bar([],[]) -bar([1,2,3],[1,2,3],2,3) -bar([1,2,3],[1,2,3],[1,2,3],[1,2,3]) -show() -)"; - -static const char * s_pyplotBarErrorScript = R"(# -from matplotlib.pyplot import * -bar([1,2,3],[1,2,3,4],[1,2,3],[1,2,3]) -show() -)"; - QUIZ_CASE(python_matplotlib_pyplot_bar) { - assert_script_execution_succeeds(s_pyplotBarScript); - assert_script_execution_fails(s_pyplotBarErrorScript); + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from matplotlib.pyplot import *"); + assert_command_execution_succeeds(env, "bar([0,2,3],[10,12,23])"); + assert_command_execution_succeeds(env, "bar([0,2,3],10)"); + assert_command_execution_succeeds(env, "bar([],[])"); + assert_command_execution_succeeds(env, "bar([1,2,3],[1,2,3],2,3)"); + assert_command_execution_succeeds(env, "bar([1,2,3],[1,2,3],[1,2,3],[1,2,3])"); + assert_command_execution_succeeds(env, "show()"); + assert_command_execution_fails(env, "bar([1,2,3],[1,2,3,4],[1,2,3],[1,2,3])"); + deinit_environment(); } -static const char * s_pyplotGridScript = R"(# -from matplotlib.pyplot import * -grid(True) -grid() -show() -)"; - QUIZ_CASE(python_matplotlib_pyplot_grid) { - assert_script_execution_succeeds(s_pyplotGridScript); + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from matplotlib.pyplot import *"); + assert_command_execution_succeeds(env, "grid(True)"); + assert_command_execution_succeeds(env, "grid()"); + deinit_environment(); } -static const char * s_pyplotHistScript = R"(# -from matplotlib.pyplot import * -hist([2,3,4,5,6]) -hist([2,3,4,5,6],23) -hist([2,3,4,5,6],[0,2,3]) -hist([2,3,4,5,6],[0,2,3, 4,5,6,7]) -show() -)"; - QUIZ_CASE(python_matplotlib_pyplot_hist) { - assert_script_execution_succeeds(s_pyplotHistScript); + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from matplotlib.pyplot import *"); + assert_command_execution_succeeds(env, "hist([2,3,4,5,6])"); + assert_command_execution_succeeds(env, "hist([2,3,4,5,6],23)"); + assert_command_execution_succeeds(env, "hist([2,3,4,5,6],[0,2,3])"); + assert_command_execution_succeeds(env, "hist([2,3,4,5,6],[0,2,3, 4,5,6,7])"); + assert_command_execution_succeeds(env, "show()"); + deinit_environment(); } -static const char * s_pyplotPlotScript = R"(# -from matplotlib.pyplot import * -plot([2,3,4,5,6]) -plot([2,3,4,5,6],[3,4,5,6,7]) -show() -)"; - -static const char * s_pyplotPlotErrorScript = R"(# -from matplotlib.pyplot import * -plot([2,3,4,5,6],2) -)"; - QUIZ_CASE(python_matplotlib_pyplot_plot) { - assert_script_execution_succeeds(s_pyplotPlotScript); - assert_script_execution_fails(s_pyplotPlotErrorScript); + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from matplotlib.pyplot import *"); + assert_command_execution_succeeds(env, "plot([2,3,4,5,6])"); + assert_command_execution_succeeds(env, "plot(2,3)"); + assert_command_execution_succeeds(env, "plot([2,3,4,5,6],[3,4,5,6,7])"); + assert_command_execution_succeeds(env, "show()"); + assert_command_execution_fails(env, "plot([2,3,4,5,6],2)"); + deinit_environment(); } -static const char * s_pyplotScatterScript = R"(# -from matplotlib.pyplot import * -scatter(2,3) -scatter([2,3,4,5,6],[3,4,5,6,7]) -show() -)"; - -static const char * s_pyplotScatterErrorScript = R"(# -from matplotlib.pyplot import * -scatter([2,3,4,5,6],2) -)"; - QUIZ_CASE(python_matplotlib_pyplot_scatter) { - assert_script_execution_succeeds(s_pyplotScatterScript); - assert_script_execution_fails(s_pyplotScatterErrorScript); + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from matplotlib.pyplot import *"); + assert_command_execution_succeeds(env, "scatter(2,3)"); + assert_command_execution_succeeds(env, "scatter([2,3,4,5,6],[3,4,5,6,7])"); + assert_command_execution_succeeds(env, "show()"); + assert_command_execution_fails(env, "scatter([2,3,4,5,6],2)"); + deinit_environment(); } -static const char * s_pyplotTextScript = R"(# -from matplotlib.pyplot import * -text(2,3,'hello') -show() -)"; - QUIZ_CASE(python_matplotlib_pyplot_text) { - assert_script_execution_succeeds(s_pyplotTextScript); + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from matplotlib.pyplot import *"); + assert_command_execution_succeeds(env, "text(2,3,'hello')"); + assert_command_execution_succeeds(env, "show()"); + deinit_environment(); } From ec5803f06f2468c3b2f114c71f15b2e3df6cf10c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 1 Apr 2020 15:40:51 +0200 Subject: [PATCH 12/79] [python] Test: fix printText --- python/test/execution_environment.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/test/execution_environment.cpp b/python/test/execution_environment.cpp index d606695c6..4671d6a00 100644 --- a/python/test/execution_environment.cpp +++ b/python/test/execution_environment.cpp @@ -7,7 +7,8 @@ char TestExecutionEnvironment::s_pythonHeap[TestExecutionEnvironment::s_pythonHe void TestExecutionEnvironment::printText(const char * text, size_t length) { assert(m_printTextIndex + length < k_maxPrintedTextSize); - m_printTextIndex += strlcpy(m_printTextBuffer + m_printTextIndex, text, length + 1); + strlcpy(m_printTextBuffer + m_printTextIndex, text, length + 1); + m_printTextIndex += length; m_printTextBuffer[m_printTextIndex] = 0; } From cdff61f64bd4cb589fee412735c3301b00a59325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 1 Apr 2020 15:41:48 +0200 Subject: [PATCH 13/79] [python] Add tests for each module --- python/Makefile | 6 ++++++ python/test/ion.cpp | 25 +++++++++++++++++++++++++ python/test/kandinsky.cpp | 28 ++++++++++++++++++++++++++++ python/test/math.cpp | 19 +++++++++++++++++++ python/test/random.cpp | 11 +++++++++++ python/test/time.cpp | 12 ++++++++++++ python/test/turtle.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 139 insertions(+) create mode 100644 python/test/ion.cpp create mode 100644 python/test/kandinsky.cpp create mode 100644 python/test/math.cpp create mode 100644 python/test/random.cpp create mode 100644 python/test/time.cpp create mode 100644 python/test/turtle.cpp diff --git a/python/Makefile b/python/Makefile index cf3ed9764..1f76017e3 100644 --- a/python/Makefile +++ b/python/Makefile @@ -191,5 +191,11 @@ $(call object_for,$(python_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.gener tests_src += $(addprefix python/test/,\ basics.cpp \ execution_environment.cpp \ + ion.cpp \ + kandinsky.cpp \ + math.cpp \ + random.cpp \ + time.cpp \ + turtle.cpp \ matplotlib.cpp \ ) diff --git a/python/test/ion.cpp b/python/test/ion.cpp new file mode 100644 index 000000000..991f47a6b --- /dev/null +++ b/python/test/ion.cpp @@ -0,0 +1,25 @@ +#include +#include "execution_environment.h" + +QUIZ_CASE(python_ion_import) { + // Test "from ion import *" + TestExecutionEnvironment env = init_environement(); + assert_command_execution_fails(env, "keydown(KEY_LEFT)"); + assert_command_execution_succeeds(env, "from ion import *"); + assert_command_execution_succeeds(env, "keydown(KEY_LEFT)"); + deinit_environment(); + + // "import ion" + env = init_environement(); + assert_command_execution_fails(env, "ion.keydown(ion.KEY_LEFT)"); + assert_command_execution_succeeds(env, "import ion"); + assert_command_execution_succeeds(env, "ion.keydown(ion.KEY_LEFT)"); + deinit_environment(); +} + +QUIZ_CASE(python_ion_keydown) { + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from ion import *"); + assert_command_execution_succeeds(env, "keydown(KEY_LEFT)", "False\n"); + deinit_environment(); +} diff --git a/python/test/kandinsky.cpp b/python/test/kandinsky.cpp new file mode 100644 index 000000000..516e0d008 --- /dev/null +++ b/python/test/kandinsky.cpp @@ -0,0 +1,28 @@ +#include +#include "execution_environment.h" + +QUIZ_CASE(python_kandinsky_import) { + // Test "from kandinsky import *" + TestExecutionEnvironment env = init_environement(); + assert_command_execution_fails(env, "get_pixel(0,0)"); + assert_command_execution_succeeds(env, "from kandinsky import *"); + assert_command_execution_succeeds(env, "get_pixel(0,0)"); + deinit_environment(); + + // "import kandinsky" + env = init_environement(); + assert_command_execution_fails(env, "kandinsky.get_pixel(0,0)"); + assert_command_execution_succeeds(env, "import kandinsky"); + assert_command_execution_succeeds(env, "kandinsky.get_pixel(0,0)"); + deinit_environment(); +} + +QUIZ_CASE(python_kandinsky_basics) { + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from kandinsky import *"); + assert_command_execution_succeeds(env, "set_pixel(0,0,color(12,12,12))"); + assert_command_execution_succeeds(env, "get_pixel(0,0)"); + assert_command_execution_succeeds(env, "fill_rect(0,0,10,10,color(2,3,2))"); + assert_command_execution_succeeds(env, "draw_string('hello',0,0)"); + deinit_environment(); +} diff --git a/python/test/math.cpp b/python/test/math.cpp new file mode 100644 index 000000000..f13c26139 --- /dev/null +++ b/python/test/math.cpp @@ -0,0 +1,19 @@ +#include +#include "execution_environment.h" + +QUIZ_CASE(python_math) { + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from math import *"); + assert_command_execution_succeeds(env, "e", "2.718281828459045\n"); + assert_command_execution_succeeds(env, "gamma(9)", "40320.0\n"); + deinit_environment(); +} + +QUIZ_CASE(python_cmath) { + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from cmath import *"); + assert_command_execution_succeeds(env, "cos(0)", "(1+-0j)\n"); + deinit_environment(); +} + + diff --git a/python/test/random.cpp b/python/test/random.cpp new file mode 100644 index 000000000..9ff3acb09 --- /dev/null +++ b/python/test/random.cpp @@ -0,0 +1,11 @@ +#include +#include "execution_environment.h" + +QUIZ_CASE(python_random) { + TestExecutionEnvironment env = init_environement(); + assert_command_execution_fails(env, "random()"); + assert_command_execution_succeeds(env, "from random import *"); + assert_command_execution_succeeds(env, "random()"); + assert_command_execution_succeeds(env, "getrandbits(23)"); + deinit_environment(); +} diff --git a/python/test/time.cpp b/python/test/time.cpp new file mode 100644 index 000000000..a2baa3fa0 --- /dev/null +++ b/python/test/time.cpp @@ -0,0 +1,12 @@ +#include +#include "execution_environment.h" + +QUIZ_CASE(python_time) { + TestExecutionEnvironment env = init_environement(); + assert_command_execution_fails(env, "monotonic()"); + assert_command_execution_succeeds(env, "from time import *"); + assert_command_execution_succeeds(env, "monotonic()"); + assert_command_execution_succeeds(env, "sleep(23)"); + deinit_environment(); +} + diff --git a/python/test/turtle.cpp b/python/test/turtle.cpp new file mode 100644 index 000000000..e5f5572af --- /dev/null +++ b/python/test/turtle.cpp @@ -0,0 +1,38 @@ +#include +#include "execution_environment.h" + +// TODO: to be completed + +QUIZ_CASE(python_turtle) { + TestExecutionEnvironment env = init_environement(); + assert_command_execution_fails(env, "forward(3)"); + assert_command_execution_succeeds(env, "from turtle import *"); + assert_command_execution_succeeds(env, "reset()"); + assert_command_execution_succeeds(env, "forward(10)"); + assert_command_execution_succeeds(env, "backward(5)"); + assert_command_execution_succeeds(env, "right(10)"); + assert_command_execution_succeeds(env, "left(5)"); + assert_command_execution_succeeds(env, "circle(28)"); + assert_command_execution_succeeds(env, "speed(28)"); + assert_command_execution_succeeds(env, "goto(28,28)"); + assert_command_execution_succeeds(env, "position()", "(28.0, 28.0)\n"); + assert_command_execution_succeeds(env, "setheading(28)"); + assert_command_execution_succeeds(env, "heading()", "28.0\n"); + assert_command_execution_succeeds(env, "pendown()"); + assert_command_execution_succeeds(env, "penup()"); + assert_command_execution_succeeds(env, "pensize(2)"); + assert_command_execution_succeeds(env, "isdown()", "False\n"); + assert_command_execution_succeeds(env, "showturtle()"); + deinit_environment(); +} + +QUIZ_CASE(python_turtle_circle) { + // Turtle position should be unchanged after a complete circle + TestExecutionEnvironment env = init_environement(); + assert_command_execution_succeeds(env, "from turtle import *"); + assert_command_execution_succeeds(env, "goto(0,0)"); + assert_command_execution_succeeds(env, "circle(28)"); + // FIXME: this is actually not true + //assert_command_execution_succeeds(env, "position()", "(0.0, 0.0)\n"); + deinit_environment(); +} From 8a5af352bf94a6a40ea5d91e03ad4516a7549e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 1 Apr 2020 16:12:47 +0200 Subject: [PATCH 14/79] [python] Fix warning: signed/unsigned int comparison --- python/test/execution_environment.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/test/execution_environment.cpp b/python/test/execution_environment.cpp index 4671d6a00..7fb6ae91c 100644 --- a/python/test/execution_environment.cpp +++ b/python/test/execution_environment.cpp @@ -22,7 +22,7 @@ void inlineToBeSingleInput(char * buffer, size_t bufferSize, const char * script bufferChar += strlcpy(buffer, openExec, bufferSize); const char * scriptChar = script; while (*scriptChar != 0) { - assert(bufferChar - buffer + 2 < bufferSize - 1); + assert(bufferChar - buffer + 2 < (int)bufferSize - 1); if (*scriptChar == '\n') { // Turn carriage return in {'\', 'n'} to be processed by exec *bufferChar++ = '\\'; @@ -33,7 +33,7 @@ void inlineToBeSingleInput(char * buffer, size_t bufferSize, const char * script scriptChar++; } bufferChar += strlcpy(bufferChar, closeExec, buffer + bufferSize - bufferChar); - assert(bufferChar - buffer < bufferSize); + assert(bufferChar - buffer < (int)bufferSize); *bufferChar = 0; } From 40986ebb99f073dde87405954f5f6a2c4f21da15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 1 Apr 2020 16:13:11 +0200 Subject: [PATCH 15/79] [python] Change math module test to be true on all platforms --- python/test/math.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/test/math.cpp b/python/test/math.cpp index f13c26139..e7e4c06f7 100644 --- a/python/test/math.cpp +++ b/python/test/math.cpp @@ -5,7 +5,7 @@ QUIZ_CASE(python_math) { TestExecutionEnvironment env = init_environement(); assert_command_execution_succeeds(env, "from math import *"); assert_command_execution_succeeds(env, "e", "2.718281828459045\n"); - assert_command_execution_succeeds(env, "gamma(9)", "40320.0\n"); + assert_command_execution_succeeds(env, "gamma(3)", "2.0\n"); deinit_environment(); } From e6516ae0e7e8ff05432dba8d4a12c8eb3f98ae3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 2 Apr 2020 15:02:01 +0200 Subject: [PATCH 16/79] [apps/calculation] Fix typo --- apps/calculation/additional_outputs/complex_graph_cell.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/calculation/additional_outputs/complex_graph_cell.cpp b/apps/calculation/additional_outputs/complex_graph_cell.cpp index 0d0bcf6fe..46dee7f6d 100644 --- a/apps/calculation/additional_outputs/complex_graph_cell.cpp +++ b/apps/calculation/additional_outputs/complex_graph_cell.cpp @@ -71,7 +71,7 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const { // 're(z)' label drawLabel(ctx, rect, real, 0.0f, "re(z)", Palette::Red, CurveView::RelativePosition::None, imag >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After); // 'im(z)' label - drawLabel(ctx, rect, 0.0f, imag, "im(θ)", Palette::Red, real >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After, CurveView::RelativePosition::None); + drawLabel(ctx, rect, 0.0f, imag, "im(z)", Palette::Red, real >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After, CurveView::RelativePosition::None); // '|z|' label, the relative horizontal position of this label depends on the quadrant CurveView::RelativePosition verticalPosition = real*imag < 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After; if (real == 0.0f) { From 90222c3f45ef073df7072089adecef5ba502f31f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 2 Apr 2020 16:07:54 +0200 Subject: [PATCH 17/79] [apps/calculation] SelectableTableView: scrollToCell once the margin have been updated This fixes the following bug: input the vertical matrix [1,2,3], the scroll failed --- apps/calculation/selectable_table_view.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/calculation/selectable_table_view.cpp b/apps/calculation/selectable_table_view.cpp index e2d81e53c..12599842b 100644 --- a/apps/calculation/selectable_table_view.cpp +++ b/apps/calculation/selectable_table_view.cpp @@ -12,12 +12,12 @@ CalculationSelectableTableView::CalculationSelectableTableView(Responder * paren } void CalculationSelectableTableView::scrollToCell(int i, int j) { - ::SelectableTableView::scrollToCell(i, j); if (m_contentView.bounds().height() < bounds().height()) { setTopMargin(bounds().height() - m_contentView.bounds().height()); } else { setTopMargin(0); } + ::SelectableTableView::scrollToCell(i, j); ScrollView::layoutSubviews(); if (m_contentView.bounds().height() - contentOffset().y() < bounds().height()) { KDCoordinate contentOffsetX = contentOffset().x(); From 300de8fb15aa11cd0731288e39593436caf98366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 2 Apr 2020 17:09:07 +0200 Subject: [PATCH 18/79] [apps/calculation] HistoryViewCell: when changing SubviewType, set the right subcell as first responder! This fixes the following bug: when selecting a cell whose content is too long to be displayed, the scrolling is broken --- apps/calculation/history_view_cell.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index e706d847f..9210a2984 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -30,6 +30,7 @@ void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType, if (selectedCell) { selectedCell->reloadSubviewHighlight(); selectedCell->cellDidSelectSubview(subviewType, previousSubviewType); + Container::activeApp()->setFirstResponder(selectedCell); } if (previouslySelectedCell) { previouslySelectedCell->cellDidSelectSubview(SubviewType::Input); From 8d621e4dce13f323785a7a93d4f08317ae1ce2cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 3 Apr 2020 10:23:42 +0200 Subject: [PATCH 19/79] [apps/shared] ScrollableMultipleExpressionsView: reload scroll content view layout when changing the displayed expression layouts Fix bug: input (transpose([1 1 1 1 1 1])[1 1 1 1 1 1])^30, click on 'OK' and then 'up'. The cell is mis-displayed. --- apps/shared/scrollable_multiple_expressions_view.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/shared/scrollable_multiple_expressions_view.cpp b/apps/shared/scrollable_multiple_expressions_view.cpp index f61000837..987a8e220 100644 --- a/apps/shared/scrollable_multiple_expressions_view.cpp +++ b/apps/shared/scrollable_multiple_expressions_view.cpp @@ -168,7 +168,10 @@ void AbstractScrollableMultipleExpressionsView::setLayouts(Poincare::Layout left } if (updateLeftLayout || updateCenterLayout || updateRightLayout) { contentCell()->reloadTextColor(); + // Reload expressions layouts contentCell()->layoutSubviews(); + // Reload the scroll content view layout (the content size might have changed) + layoutSubviews(); // Do no reload scroll here as 'setLayouts' is called every time the table is re-layout (when scrolling for instance) } } From ada205b1a739810e563df2881bcafb188cd68c08 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 2 Apr 2020 15:38:08 -0400 Subject: [PATCH 20/79] [build] Use variants - Move helpers functions into build/helpers.mak - Move official warning into official targets only --- Makefile | 36 ++++------- apps/Makefile | 39 ++++-------- apps/calculation/Makefile | 2 +- apps/code/Makefile | 2 +- apps/graph/Makefile | 2 +- apps/hardware_test/Makefile | 2 +- apps/home/Makefile | 2 +- apps/on_boarding/Makefile | 2 +- apps/probability/Makefile | 2 +- apps/regression/Makefile | 2 +- apps/sequence/Makefile | 2 +- apps/settings/Makefile | 27 +++------ apps/shared/Makefile | 2 +- apps/solver/Makefile | 2 +- apps/statistics/Makefile | 2 +- apps/usb/Makefile | 2 +- build/config.mak | 13 ---- build/defaults.mak | 5 ++ build/helpers.mk | 36 +++++++++++ build/rules.mk | 42 +++++++------ build/targets.device.mak | 29 ++------- build/targets.mak | 82 +++++++++++++++++++------- build/targets.simulator.mak | 10 ++-- build/variants.mak | 63 ++++++++++++++++++++ ion/Makefile | 16 +---- ion/src/device/Makefile | 14 +---- ion/src/device/bench/Makefile | 4 +- ion/src/device/flasher/Makefile | 10 +--- ion/src/device/shared/drivers/Makefile | 6 +- ion/src/device/shared/usb/Makefile | 47 ++++++++------- ion/src/simulator/Makefile | 31 ++++------ 31 files changed, 294 insertions(+), 242 deletions(-) create mode 100644 build/helpers.mk create mode 100644 build/variants.mak diff --git a/Makefile b/Makefile index 46acaaa5a..c416c3133 100644 --- a/Makefile +++ b/Makefile @@ -1,34 +1,20 @@ -include build/config.mak - # Disable default Make rules .SUFFIXES: -object_for = $(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(basename $(1)))) - # Define the default recipe - default: -# Define a standard rule helper -# If passed a last parameter value of with_local_version, we also define an -# extra rule that can build source files within the $(BUILD_DIR). This is useful -# for rules that can be applied for intermediate objects (for example, when -# going .png -> .cpp -> .o). +include build/config.mak +include build/defaults.mak +include build/platform.$(PLATFORM).mak +include build/toolchain.$(TOOLCHAIN).mak +include build/variants.mak +include build/helpers.mk -define rule_label -@ echo "$(shell printf "%-8s" $(strip $(1)))$(@:$(BUILD_DIR)/%=%)" +define foo endef -define rule_for -ifeq ($(strip $(5)),with_local_version) -$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(addprefix $$(BUILD_DIR)/,$(strip $(3))) | $(if $(findstring official,${MAKECMDGOALS}),official_authorization) - @ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)" - $(Q) $(4) -endif -$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(strip $(3)) | $$$$(@D)/. $(if $(findstring official,${MAKECMDGOALS}),official_authorization) - @ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)" - $(Q) $(4) -endef +$(eval $(call foo)) .PHONY: info info: @@ -83,7 +69,9 @@ $(BUILD_DIR)%/.: # Each sub-Makefile can either add sources to $(%_src) variables or define a # new executable target. The $(%_src) variables list the sources that can be # built and linked to executables being generated. - +ifndef USE_LIBA + $(error platform.mak should define USE_LIBA) +endif ifeq ($(USE_LIBA),0) include liba/Makefile.bridge else @@ -102,7 +90,7 @@ include build/struct_layout/Makefile include build/scenario/Makefile include quiz/Makefile # Quiz needs to be included at the end -all_src = $(apps_all_src) $(escher_src) $(ion_all_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(poincare_src) $(python_src) $(runner_src) $(ion_target_device_flasher_light_src) $(ion_target_device_flasher_verbose_src) $(ion_target_device_bench_src) $(tests_src) +all_src = $(apps_src) $(escher_src) $(ion_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(poincare_src) $(python_src) $(runner_src) $(ion_device_flasher_src) $(ion_device_bench_src) $(tests_src) all_objs = $(call object_for,$(all_src)) .SECONDARY: $(all_objs) diff --git a/apps/Makefile b/apps/Makefile index dc9e4c63e..c1402113f 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -10,8 +10,14 @@ apps = # (path to the apps header). $(foreach i,${EPSILON_APPS},$(eval include apps/$(i)/Makefile)) -app_src += $(addprefix apps/,\ +apps_src += $(addprefix apps/,\ apps_container.cpp \ + apps_container_launch_default.cpp:-onboarding \ + apps_container_launch_on_boarding.cpp:+onboarding \ + apps_container_prompt_beta.cpp:+beta \ + apps_container_prompt_none.cpp:-beta \ + apps_container_prompt_none.cpp:-update \ + apps_container_prompt_update.cpp:+update \ apps_container_storage.cpp \ apps_window.cpp \ backlight_dimming_timer.cpp \ @@ -19,6 +25,8 @@ app_src += $(addprefix apps/,\ battery_view.cpp \ empty_battery_window.cpp \ exam_pop_up_controller.cpp \ + exam_mode_configuration_official.cpp:+official \ + exam_mode_configuration_non_official.cpp:-official \ global_preferences.cpp \ i18n.py \ lock_view.cpp \ @@ -32,14 +40,6 @@ app_src += $(addprefix apps/,\ ) tests_src += apps/exam_mode_configuration_official.cpp -apps_official += apps/exam_mode_configuration_official.cpp -apps_non_official += apps/exam_mode_configuration_non_official.cpp - -apps_launch_on_boarding_src += apps/apps_container_launch_on_boarding.cpp -apps_launch_default_src += apps/apps_container_launch_default.cpp -apps_prompt_none_src += apps/apps_container_prompt_none.cpp -apps_prompt_beta_src += apps/apps_container_prompt_beta.cpp -apps_prompt_update_src += apps/apps_container_prompt_update.cpp snapshots_declaration = $(foreach i,$(apps),$(i)::Snapshot m_snapshot$(subst :,,$(i))Snapshot;) apps_declaration = $(foreach i,$(apps),$(i) m_$(subst :,,$(i));) @@ -89,28 +89,11 @@ $(BUILD_DIR)/apps/i18n.h: $(BUILD_DIR)/apps/i18n.cpp $(eval $(call depends_on_image,apps/title_bar_view.cpp,apps/exam_icon.png)) -all_app_src = $(app_src)(apps_launch_on_boarding_src) $(apps_launch_default_src) $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src) $(apps_official) $(apps_non_official) $(tests_src) - -$(call object_for,$(all_app_src)): $(BUILD_DIR)/apps/i18n.h -$(call object_for,$(all_app_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h +$(call object_for,$(apps_src) $(tests_src)): $(BUILD_DIR)/apps/i18n.h +$(call object_for,$(apps_src) $(tests_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h apps_tests_src = $(app_calculation_test_src) $(app_code_test_src) $(app_probability_test_src) $(app_regression_test_src) $(app_sequence_test_src) $(app_shared_test_src) $(app_statistics_test_src) $(app_settings_test_src) $(app_solver_test_src) apps_tests_src += $(addprefix apps/,\ global_preferences.cpp \ ) - -# Configure variants -apps_all_src = $(app_src) -apps_all_src += $(apps_official) $(apps_non_official) -apps_all_src += $(apps_launch_default_src) $(apps_launch_on_boarding_src) -apps_all_src += $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src) - -apps_default_src = $(app_src) $(apps_non_official) $(apps_launch_default_src) $(apps_prompt_none_src) -apps_official_default_src = $(app_src) $(apps_official) $(apps_launch_default_src) $(apps_prompt_none_src) -apps_onboarding_src = $(app_src) $(apps_non_official) $(apps_launch_on_boarding_src) $(apps_prompt_none_src) -apps_official_onboarding_src = $(app_src) $(apps_official) $(apps_launch_on_boarding_src) $(apps_prompt_none_src) -apps_onboarding_update_src = $(app_src) $(apps_non_official) $(apps_launch_on_boarding_src) $(apps_prompt_update_src) -apps_official_onboarding_update_src = $(app_src) $(apps_official) $(apps_launch_on_boarding_src) $(apps_prompt_update_src) -apps_onboarding_beta_src = $(app_src) $(apps_non_official) $(apps_launch_on_boarding_src) $(apps_prompt_beta_src) -apps_official_onboarding_beta_src = $(app_src) $(apps_official) $(apps_launch_on_boarding_src) $(apps_prompt_beta_src) diff --git a/apps/calculation/Makefile b/apps/calculation/Makefile index bd0615e4a..ef4c9d36d 100644 --- a/apps/calculation/Makefile +++ b/apps/calculation/Makefile @@ -30,7 +30,7 @@ app_calculation_src = $(addprefix apps/calculation/,\ ) app_calculation_src += $(app_calculation_test_src) -app_src += $(app_calculation_src) +apps_src += $(app_calculation_src) i18n_files += $(addprefix apps/calculation/,\ base.de.i18n\ diff --git a/apps/code/Makefile b/apps/code/Makefile index 33b60f68b..aa61accc9 100644 --- a/apps/code/Makefile +++ b/apps/code/Makefile @@ -27,7 +27,7 @@ app_code_src = $(addprefix apps/code/,\ ) app_code_src += $(app_code_test_src) -app_src += $(app_code_src) +apps_src += $(app_code_src) i18n_files += $(addprefix apps/code/,\ base.de.i18n\ diff --git a/apps/graph/Makefile b/apps/graph/Makefile index 5284a2dac..482528ad2 100644 --- a/apps/graph/Makefile +++ b/apps/graph/Makefile @@ -31,7 +31,7 @@ app_graph_src = $(addprefix apps/graph/,\ values/values_controller.cpp \ ) -app_src += $(app_graph_src) +apps_src += $(app_graph_src) i18n_files += $(addprefix apps/graph/,\ base.de.i18n\ diff --git a/apps/hardware_test/Makefile b/apps/hardware_test/Makefile index 339b8887c..960ebcc3c 100644 --- a/apps/hardware_test/Makefile +++ b/apps/hardware_test/Makefile @@ -16,4 +16,4 @@ app_hardware_test_src = $(addprefix apps/hardware_test/,\ vblank_test_controller.cpp \ ) -app_src += $(app_hardware_test_src) +apps_src += $(app_hardware_test_src) diff --git a/apps/home/Makefile b/apps/home/Makefile index 5f4b7eb59..d052cada2 100644 --- a/apps/home/Makefile +++ b/apps/home/Makefile @@ -4,7 +4,7 @@ app_home_src = $(addprefix apps/home/,\ controller.cpp \ ) -app_src += $(app_home_src) +apps_src += $(app_home_src) i18n_files += $(addprefix apps/home/,\ base.de.i18n \ diff --git a/apps/on_boarding/Makefile b/apps/on_boarding/Makefile index 723d19d76..7942bd97f 100644 --- a/apps/on_boarding/Makefile +++ b/apps/on_boarding/Makefile @@ -7,7 +7,7 @@ app_on_boarding_src = $(addprefix apps/on_boarding/,\ power_on_self_test.cpp \ ) -app_src += $(app_on_boarding_src) +apps_src += $(app_on_boarding_src) i18n_files += $(addprefix apps/on_boarding/,\ base.de.i18n\ diff --git a/apps/probability/Makefile b/apps/probability/Makefile index 436828add..c2d1080dc 100644 --- a/apps/probability/Makefile +++ b/apps/probability/Makefile @@ -38,7 +38,7 @@ app_probability_src = $(addprefix apps/probability/,\ ) app_probability_src += $(app_probability_test_src) -app_src += $(app_probability_src) +apps_src += $(app_probability_src) i18n_files += $(addprefix apps/probability/,\ base.de.i18n\ diff --git a/apps/regression/Makefile b/apps/regression/Makefile index 580f13cd4..2fd71b5a0 100644 --- a/apps/regression/Makefile +++ b/apps/regression/Makefile @@ -37,7 +37,7 @@ app_regression_src = $(addprefix apps/regression/,\ ) app_regression_src += $(app_regression_test_src) -app_src += $(app_regression_src) +apps_src += $(app_regression_src) i18n_files += $(addprefix apps/regression/,\ base.de.i18n\ diff --git a/apps/sequence/Makefile b/apps/sequence/Makefile index ecdb7326b..4320815e7 100644 --- a/apps/sequence/Makefile +++ b/apps/sequence/Makefile @@ -26,7 +26,7 @@ app_sequence_src = $(addprefix apps/sequence/,\ ) app_sequence_src += $(app_sequence_test_src) -app_src += $(app_sequence_src) +apps_src += $(app_sequence_src) i18n_files += $(addprefix apps/sequence/,\ base.de.i18n\ diff --git a/apps/settings/Makefile b/apps/settings/Makefile index b8197213e..5334f862c 100644 --- a/apps/settings/Makefile +++ b/apps/settings/Makefile @@ -5,7 +5,15 @@ app_settings_src = $(addprefix apps/settings/,\ app.cpp \ cell_with_separator.cpp \ main_controller.cpp \ + main_controller_prompt_beta.cpp:+beta \ + main_controller_prompt_none.cpp:-beta \ + main_controller_prompt_none.cpp:-update \ + main_controller_prompt_update.cpp:+update \ sub_menu/about_controller.cpp \ + sub_menu/about_controller_official.cpp:+official \ + sub_menu/about_controller_non_official.cpp:-official \ + sub_menu/exam_mode_controller_official.cpp:+official \ + sub_menu/exam_mode_controller_non_official.cpp:-official \ sub_menu/display_mode_controller.cpp \ sub_menu/exam_mode_controller.cpp \ sub_menu/generic_sub_controller.cpp \ @@ -15,24 +23,7 @@ app_settings_src = $(addprefix apps/settings/,\ ) app_settings_src += $(app_settings_test_src) -app_src += $(app_settings_src) - -apps_prompt_none_src += apps/settings/main_controller_prompt_none.cpp -apps_prompt_beta_src += apps/settings/main_controller_prompt_beta.cpp -apps_prompt_update_src += apps/settings/main_controller_prompt_update.cpp - -apps_settings_official += $(addprefix apps/settings/,\ - sub_menu/about_controller_official.cpp \ - sub_menu/exam_mode_controller_official.cpp \ -) - -apps_settings_non_official += $(addprefix apps/settings/,\ - sub_menu/about_controller_non_official.cpp \ - sub_menu/exam_mode_controller_non_official.cpp \ -) - -apps_official += $(apps_settings_official) -apps_non_official += $(apps_settings_non_official) +apps_src += $(app_settings_src) i18n_files += $(addprefix apps/settings/,\ base.de.i18n\ diff --git a/apps/shared/Makefile b/apps/shared/Makefile index b406d41cb..4056c9dbd 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -84,4 +84,4 @@ app_shared_src = $(addprefix apps/shared/,\ ) app_shared_src += $(app_shared_test_src) -app_src += $(app_shared_src) +apps_src += $(app_shared_src) diff --git a/apps/solver/Makefile b/apps/solver/Makefile index 05273744b..f9fea9bf3 100644 --- a/apps/solver/Makefile +++ b/apps/solver/Makefile @@ -16,7 +16,7 @@ app_solver_src = $(addprefix apps/solver/,\ ) app_solver_src += $(app_solver_test_src) -app_src += $(app_solver_src) +apps_src += $(app_solver_src) i18n_files += $(addprefix apps/solver/,\ base.de.i18n\ diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index c1b0c158a..34e9cb878 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -27,7 +27,7 @@ app_statistics_src = $(addprefix apps/statistics/,\ ) app_statistics_src += $(app_statistics_test_src) -app_src += $(app_statistics_src) +apps_src += $(app_statistics_src) i18n_files += $(addprefix apps/statistics/,\ base.de.i18n\ diff --git a/apps/usb/Makefile b/apps/usb/Makefile index c539f947c..4e4a6449b 100644 --- a/apps/usb/Makefile +++ b/apps/usb/Makefile @@ -3,7 +3,7 @@ app_usb_src = $(addprefix apps/usb/,\ usb_connected_controller.cpp \ ) -app_src += $(app_usb_src) +apps_src += $(app_usb_src) i18n_files += $(addprefix apps/usb/,\ base.de.i18n\ diff --git a/build/config.mak b/build/config.mak index 9c8ff7b5d..1d2822bcd 100644 --- a/build/config.mak +++ b/build/config.mak @@ -3,22 +3,9 @@ PLATFORM ?= device DEBUG ?= 0 -include build/defaults.mak -include build/platform.$(PLATFORM).mak - EPSILON_VERSION ?= 13.0.0 EPSILON_APPS ?= calculation graph code statistics probability solver sequence regression settings EPSILON_I18N ?= en fr es de pt EPSILON_GETOPT ?= 0 EPSILON_TELEMETRY ?= 0 ESCHER_LOG_EVENTS_BINARY ?= 0 - -ifndef USE_LIBA - $(error platform.mak should define USE_LIBA) -endif -include build/toolchain.$(TOOLCHAIN).mak - -SFLAGS += -DDEBUG=$(DEBUG) -SFLAGS += -DEPSILON_GETOPT=$(EPSILON_GETOPT) -SFLAGS += -DEPSILON_TELEMETRY=$(EPSILON_TELEMETRY) -SFLAGS += -DESCHER_LOG_EVENTS_BINARY=$(ESCHER_LOG_EVENTS_BINARY) diff --git a/build/defaults.mak b/build/defaults.mak index d8a0c8c0d..6a4ad3e0b 100644 --- a/build/defaults.mak +++ b/build/defaults.mak @@ -2,6 +2,11 @@ HOSTCC = gcc HOSTCXX = g++ PYTHON = python3 +SFLAGS += -DDEBUG=$(DEBUG) +SFLAGS += -DEPSILON_GETOPT=$(EPSILON_GETOPT) +SFLAGS += -DEPSILON_TELEMETRY=$(EPSILON_TELEMETRY) +SFLAGS += -DESCHER_LOG_EVENTS_BINARY=$(ESCHER_LOG_EVENTS_BINARY) + # Language-specific flags CFLAGS = -std=c99 CXXFLAGS = -std=c++11 -fno-exceptions -fno-rtti -fno-threadsafe-statics diff --git a/build/helpers.mk b/build/helpers.mk new file mode 100644 index 000000000..f0b68cf28 --- /dev/null +++ b/build/helpers.mk @@ -0,0 +1,36 @@ +# Define a standard rule helper +# If passed a last parameter value of with_local_version, we also define an +# extra rule that can build source files within the $(BUILD_DIR). This is useful +# for rules that can be applied for intermediate objects (for example, when +# going .png -> .cpp -> .o). + +define rule_label +@ echo "$(shell printf "%-8s" $(strip $(1)))$(@:$(BUILD_DIR)/%=%)" +endef + +define rule_for +ifeq ($(strip $(5)),with_local_version) +$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(addprefix $$(BUILD_DIR)/,$(strip $(3))) + @ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)" + $(Q) $(4) +endif +$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(strip $(3)) | $$$$(@D)/. + @ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)" + $(Q) $(4) +endef + +# Helper functions to work with variants + +define direct_object_for +$(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(basename $(1)))) +endef + +# Objects for source files in $(1) matching flavor $(2). A flavor is a dot +# separated list of variants (e.g. large.speed). +define flavored_object_for +$(call direct_object_for,$(call filter_variants,$(1),$(sort $(subst ., ,$(2))))) +endef + +define object_for +$(call direct_object_for,$(call any_variant,$(1))) +endef diff --git a/build/rules.mk b/build/rules.mk index d08285bc5..2826a3746 100644 --- a/build/rules.mk +++ b/build/rules.mk @@ -1,16 +1,5 @@ # Define standard compilation rules -.PHONY: official_authorization -ifeq ($(ACCEPT_OFFICIAL_TOS),1) -official_authorization: -else -official_authorization: - @echo "CAUTION: You are trying to build an official NumWorks firmware." - @echo "Distribution of such firmware by a third party is prohibited." - @echo "Please set the ACCEPT_OFFICIAL_TOS environment variable to proceed." - @exit -1 -endif - $(eval $(call rule_for, \ AS, %.o, %.s, \ $$(CC) $$(SFLAGS) -c $$< -o $$@ \ @@ -22,12 +11,34 @@ $(eval $(call rule_for, \ with_local_version \ )) +$(eval $(call rule_for, \ + CPP, %, %.inc, \ + $$(CPP) -P $$< $$@ \ +)) + $(eval $(call rule_for, \ CXX, %.o, %.cpp, \ $$(CXX) $$(CXXFLAGS) $$(SFLAGS) -c $$< -o $$@, \ with_local_version \ )) +$(eval $(call rule_for, \ + DFUSE, %.dfu, %.elf, \ + $$(PYTHON) build/device/elf2dfu.py $$< $$@, \ + with_local_version \ +)) + +$(eval $(call rule_for, \ + OBJCOPY, %.hex, %.elf, \ + $$(OBJCOPY) -O ihex $$< $$@ \ +)) + +$(eval $(call rule_for, \ + OBJCOPY, %.bin, %.elf, \ + $$(OBJCOPY) -O binary $$< $$@, \ + with_local_version \ +)) + $(eval $(call rule_for, \ OCC, %.o, %.m, \ $$(CC) $$(CFLAGS) $$(SFLAGS) -c $$< -o $$@ \ @@ -39,8 +50,8 @@ $(eval $(call rule_for, \ )) $(eval $(call rule_for, \ - CPP, %, %.inc, \ - $$(CPP) -P $$< $$@ \ + WINDRES, %.o, %.rc, \ + $$(WINDRES) $$< -O coff -o $$@ \ )) ifdef EXE @@ -61,8 +72,3 @@ $(eval $(call rule_for, \ )) endif endif - -$(eval $(call rule_for, \ - WINDRES, %.o, %.rc, \ - $$(WINDRES) $$< -O coff -o $$@ \ -)) diff --git a/build/targets.device.mak b/build/targets.device.mak index 3a54b7554..3ad973562 100644 --- a/build/targets.device.mak +++ b/build/targets.device.mak @@ -3,23 +3,6 @@ include build/targets.device.$(MODEL).mak HANDY_TARGETS += flasher.light flasher.verbose bench.ram bench.flash HANDY_TARGETS_EXTENSIONS += dfu hex bin -$(eval $(call rule_for, \ - DFUSE, %.dfu, %.$$(EXE), \ - $$(PYTHON) build/device/elf2dfu.py $$< $$@, \ - with_local_version \ -)) - -$(eval $(call rule_for, \ - OBJCOPY, %.hex, %.$$(EXE), \ - $$(OBJCOPY) -O ihex $$< $$@ \ -)) - -$(eval $(call rule_for, \ - OBJCOPY, %.bin, %.$$(EXE), \ - $$(OBJCOPY) -O binary $$< $$@, \ - with_local_version \ -)) - .PHONY: %_size %_size: $(BUILD_DIR)/%.$(EXE) @echo "========= BUILD OUTPUT ========" @@ -47,16 +30,16 @@ openocd: # The flasher target is defined here because otherwise $(%_src) has not been # fully filled +flasher_src = $(ion_src) $(ion_device_flasher_src) $(liba_src) $(kandinsky_src) +$(BUILD_DIR)/flasher.light.$(EXE): $(call flavored_object_for,$(flasher_src),light usbxip) +$(BUILD_DIR)/flasher.verbose.$(EXE): $(call flavored_object_for,$(flasher_src),usbxip) $(BUILD_DIR)/flasher.%.$(EXE): LDFLAGS += -Lion/src/$(PLATFORM)/flasher $(BUILD_DIR)/flasher.%.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/shared/ram.ld -flasher_base_src = $(ion_xip_src) $(liba_src) $(kandinsky_src) -$(BUILD_DIR)/flasher.light.$(EXE): $(call object_for,$(flasher_base_src) $(ion_target_device_flasher_light_src)) -$(BUILD_DIR)/flasher.verbose.$(EXE): $(call object_for,$(flasher_base_src) $(ion_target_device_flasher_verbose_src)) #TODO Do not build all apps... Put elsewhere? +bench_src = $(ion_src) $(liba_src) $(kandinsky_src) $(poincare_src) $(libaxx_src) $(app_shared_src) $(ion_device_bench_src) +$(BUILD_DIR)/bench.ram.$(EXE): $(call flavored_object_for,$(bench_src),consoleuart usbxip) $(BUILD_DIR)/bench.ram.$(EXE): LDFLAGS += -Lion/src/$(PLATFORM)/bench $(BUILD_DIR)/bench.ram.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/shared/ram.ld +$(BUILD_DIR)/bench.flash.$(EXE): $(call flavored_object_for,$(bench_src),consoleuart usbxip) $(BUILD_DIR)/bench.flash.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/$(MODEL)/internal_flash.ld -bench_src = $(ion_xip_src) $(liba_src) $(kandinsky_src) $(poincare_src) $(libaxx_src) $(app_shared_src) $(ion_target_device_bench_src) -$(BUILD_DIR)/bench.ram.$(EXE): $(call object_for,$(bench_src)) -$(BUILD_DIR)/bench.flash.$(EXE): $(call object_for,$(bench_src)) diff --git a/build/targets.mak b/build/targets.mak index ca7b9d97c..cf77a3460 100644 --- a/build/targets.mak +++ b/build/targets.mak @@ -1,28 +1,8 @@ -# Define standard Epsilon targets -base_src = $(liba_src) $(kandinsky_src) $(escher_src) $(libaxx_src) $(poincare_src) $(python_src) - -epsilon_src = $(base_src) $(ion_default_src) $(apps_default_src) -epsilon_official_src = $(base_src) $(ion_default_src) $(apps_official_default_src) - -$(BUILD_DIR)/epsilon.$(EXE): $(call object_for,$(epsilon_src)) -$(BUILD_DIR)/epsilon.official.$(EXE): $(call object_for,$(epsilon_official_src)) -$(BUILD_DIR)/epsilon.onboarding.$(EXE): $(call object_for, $(base_src) $(ion_default_src) $(apps_onboarding_src)) -$(BUILD_DIR)/epsilon.official.onboarding.$(EXE): $(call object_for,$(base_src) $(ion_default_src) $(apps_official_onboarding_src)) -$(BUILD_DIR)/epsilon.onboarding.update.$(EXE): $(call object_for, $(base_src) $(ion_default_src) $(apps_onboarding_update_src)) -$(BUILD_DIR)/epsilon.official.onboarding.update.$(EXE): $(call object_for,$(base_src) $(ion_default_src) $(apps_official_onboarding_update_src)) -$(BUILD_DIR)/epsilon.onboarding.beta.$(EXE): $(call object_for, $(base_src) $(ion_default_src) $(apps_onboarding_beta_src)) -$(BUILD_DIR)/epsilon.official.onboarding.beta.$(EXE): $(call object_for,$(base_src) $(ion_default_src) $(apps_official_onboarding_beta_src)) - -test_base_src = $(base_src) $(apps_tests_src) $(runner_src) $(tests_src) - -test_runner_src = $(test_base_src) $(ion_console_on_screen_src) -$(BUILD_DIR)/test.$(EXE): $(call object_for,$(test_runner_src)) - # Define handy targets # Those can be built easily by simply invoking "make target.ext". The named file # will be built in $(BUILD_DIR). -HANDY_TARGETS += epsilon epsilon.official epsilon.onboarding epsilon.official.onboarding epsilon.onboarding.update epsilon.official.onboarding.update epsilon.onboarding.beta epsilon.official.onboarding.beta test +HANDY_TARGETS += HANDY_TARGETS_EXTENSIONS += $(EXE) define handy_target_rule @@ -30,10 +10,70 @@ define handy_target_rule $(1).$(2): $$(BUILD_DIR)/$(1).$(2) endef +# Epsilon base target + +base_src = $(ion_src) $(liba_src) $(kandinsky_src) $(escher_src) $(libaxx_src) $(poincare_src) $(python_src) + +epsilon_src = $(base_src) $(apps_src) + +$(BUILD_DIR)/epsilon.$(EXE): $(call flavored_object_for,$(epsilon_src)) + +HANDY_TARGETS += epsilon + +# Epsilon flavored targets + +epsilon_flavors = \ + onboarding \ + onboarding.update \ + onboarding.beta + +define rule_for_epsilon_flavor +$$(BUILD_DIR)/epsilon.$(1).$$(EXE): $$(call flavored_object_for,$$(epsilon_src),$(1)) +endef + +$(foreach flavor,$(epsilon_flavors),$(eval $(call rule_for_epsilon_flavor,$(flavor)))) + +HANDY_TARGETS += $(foreach flavor,$(epsilon_flavors),epsilon.$(flavor)) + +# Epsilon official targets + +epsilon_official_flavors = \ + official \ + official.onboarding \ + official.onboarding.update \ + official.onboarding.beta + +define rule_for_unconfirmed_official_flavor +$$(BUILD_DIR)/epsilon.$(1).$$(EXE): + @echo "CAUTION: You are trying to build an official NumWorks firmware." + @echo "Distribution of such firmware by a third party is prohibited." + @echo "Please set the ACCEPT_OFFICIAL_TOS environment variable to proceed." + @exit -1 +endef + +ifeq ($(ACCEPT_OFFICIAL_TOS),1) +rule_for_official_epsilon_flavor = rule_for_epsilon_flavor +else +rule_for_official_epsilon_flavor = rule_for_unconfirmed_official_flavor +endif + +$(foreach flavor,$(epsilon_official_flavors),$(eval $(call $(rule_for_official_epsilon_flavor),$(flavor)))) + +HANDY_TARGETS += $(foreach flavor,$(epsilon_official_flavors),epsilon.$(flavor)) + +# Test + +test_runner_src = $(base_src) $(apps_tests_src) $(runner_src) $(tests_src) + +$(BUILD_DIR)/test.$(EXE): $(call flavored_object_for,$(test_runner_src),consoledisplay) + +HANDY_TARGETS += test + # Load platform-specific targets # We include them before the standard ones to give them precedence. -include build/targets.$(PLATFORM).mak +# Generate handy targets rules $(foreach extension,$(HANDY_TARGETS_EXTENSIONS),$(foreach executable,$(HANDY_TARGETS),$(eval $(call handy_target_rule,$(executable),$(extension))))) include build/targets.all.mak diff --git a/build/targets.simulator.mak b/build/targets.simulator.mak index 1d6573daf..942822600 100644 --- a/build/targets.simulator.mak +++ b/build/targets.simulator.mak @@ -1,10 +1,10 @@ # Headless targets -epsilon_headless_src = $(base_src) $(ion_headless_src) $(apps_default_src) -$(BUILD_DIR)/epsilon.headless.$(EXE): $(call object_for,$(epsilon_headless_src)) +$(eval $(call rule_for_epsilon_flavor,headless)) -test_runner_headless_src = $(test_base_src) $(ion_headless_src) -$(BUILD_DIR)/test.headless.$(EXE): $(call object_for,$(test_runner_headless_src)) +HANDY_TARGETS += epsilon.headless -HANDY_TARGETS += epsilon.headless test.headless +$(BUILD_DIR)/test.headless.$(EXE): $(call flavored_object_for,$(test_runner_src),headless) + +HANDY_TARGETS += test.headless -include build/targets.simulator.$(TARGET).mak diff --git a/build/variants.mak b/build/variants.mak new file mode 100644 index 000000000..9ac18566e --- /dev/null +++ b/build/variants.mak @@ -0,0 +1,63 @@ +# Helper functions to work with variants +# Make variants_test for an example + +define available_variants_in +$(sort $(patsubst +%,%,$(filter +%,$(subst :, ,$(1))))) +endef + +define without_any_variant_specifier +$(filter-out $(foreach variant,$(call available_variants_in,$(1)),%:-$(variant) %:+$(variant)),$(1)) +endef + +define with_variant_specifier_matching +$(foreach variant,$(3),$(patsubst %:$(1)$(variant),%,$(filter %:$(1)$(variant),$(2)))) +endef + +define without_any_variant_specifier_matching +$(filter-out \ + $(call with_variant_specifier_matching,$(1),$(2),$(3)), \ + $(foreach variant,$(call available_variants_in,$(2)),$(call with_variant_specifier_matching,$(1),$(2),$(variant))) \ +) +endef + +# Return files in $(1) that match the variant $(2) +define filter_variants +$(sort \ + $(call without_any_variant_specifier,$(1)) \ + $(call with_variant_specifier_matching,+,$(1),$(2)) \ + $(call without_any_variant_specifier_matching,-,$(1),$(2)) \ +) +endef + +# Return all files in $(1) no matter their variant +define any_variant +$(sort $(filter-out -%,$(filter-out +%,$(subst :, ,$(1))))) +endef + +# Examples + +variants_test_src = base.cpp +variants_test_src += color/green.cpp:+green +variants_test_src += color/red.cpp:+red +variants_test_src += color/blue.cpp:-red +variants_test_src += color/blue.cpp:-green +variants_test_src += engine/fast.cpp:+nitro +variants_test_src += engine/slow.cpp:-nitro + +.PHONY: variants_test +variants_test: + $(info AVAILABLE_VARIANTS_IN) + $(info --result: $(call available_variants_in,$(variants_test_src),)) + $(info --expected: green nitro red) + $(info FILTER_VARIANTS) + $(info --result: $(call filter_variants,$(variants_test_src),)) + $(info --expected: base.cpp color/blue.cpp engine/slow.cpp) + $(info FILTER_VARIANTS red) + $(info --result: $(call filter_variants,$(variants_test_src),red)) + $(info --expected: base.cpp color/red.cpp engine/slow.cpp) + $(info FILTER_VARIANTS green speed) + $(info --result: $(call filter_variants,$(variants_test_src),green nitro)) + $(info --expected: base.cpp color/green.cpp engine/fast.cpp) + $(info ANY_VARIANT) + $(info --result: $(call any_variant,$(variants_test_src))) + $(info --expected: base.cpp color/blue.cpp color/green.cpp color/red.cpp engine/fast.cpp engine/slow.cpp) diff --git a/ion/Makefile b/ion/Makefile index 7dc08544e..1c325ebc6 100644 --- a/ion/Makefile +++ b/ion/Makefile @@ -15,8 +15,6 @@ include ion/src/$(PLATFORM)/Makefile -include ion/test/$(PLATFORM)/Makefile include ion/src/shared/tools/Makefile -ion_console_display_src += ion/src/shared/console_display.cpp - # We need to work around a GCC bug (concerning versions < 5.1). It is valid in # C++11 to initialize a character array by providing a string litteral (e.g. # char test[4]= "ab"; is valid and should initialize test to 'a','b',0,0). @@ -25,10 +23,13 @@ initializer_list = $(shell echo $(1) | sed "s/\(.\)/'\1',/g")0 $(call object_for,ion/src/shared/platform_info.cpp): SFLAGS += -DPATCH_LEVEL="$(call initializer_list,$(PATCH_LEVEL))" -DEPSILON_VERSION="$(call initializer_list,$(EPSILON_VERSION))" ion_src += $(addprefix ion/src/shared/, \ + console_display.cpp:+consoledisplay \ console_line.cpp \ crc32_eat_byte.cpp \ decompress.cpp \ events.cpp \ + events_keyboard.cpp \ + events_modifier.cpp \ platform_info.cpp \ storage.cpp \ unicode/utf8_decoder.cpp\ @@ -49,14 +50,3 @@ tests_src += $(addprefix ion/test/,\ ifdef ION_STORAGE_LOG SFLAGS += -DION_STORAGE_LOG=1 endif - -# Configure variants -ion_all_src = $(ion_src) -ion_all_src += $(ion_simulator_sdl_src) $(ion_simulator_headless_src) -ion_all_src += $(ion_device_dfu_relocated_src) $(ion_device_dfu_xip_src) -ion_all_src += $(ion_console_display_src) $(ion_console_stdio_src) $(ion_console_uart_src) - -ion_default_src = $(ion_src) $(ion_simulator_sdl_src) $(ion_device_dfu_relocated_src) $(ion_console_stdio_src) -ion_console_on_screen_src = $(ion_src) $(ion_simulator_sdl_src) $(ion_device_dfu_relocated_src) $(ion_console_display_src) -ion_xip_src = $(ion_src) $(ion_simulator_sdl_src) $(ion_device_dfu_xip_src) $(ion_console_uart_src) -ion_headless_src = $(ion_src) $(ion_simulator_headless_src) $(ion_device_dfu_relocated_src) $(ion_console_stdio_src) diff --git a/ion/src/device/Makefile b/ion/src/device/Makefile index 56d222ecc..4a3a96426 100644 --- a/ion/src/device/Makefile +++ b/ion/src/device/Makefile @@ -6,22 +6,12 @@ include ion/src/device/$(MODEL)/Makefile $(call object_for,ion/src/shared/platform_info.cpp): SFLAGS += -DHEADER_SECTION="__attribute__((section(\".header\")))" -ion_src += $(addprefix ion/src/shared/, \ - console_line.cpp \ - events_keyboard.cpp \ - events_modifier.cpp \ -) - ifeq ($(EPSILON_TELEMETRY),1) ion_src += ion/src/shared/telemetry_console.cpp endif -# If you need to benchmark execution, you can replace events_keyboard with -# events_benchmark. -# If you need to profile execution, you can replace events_keyboard with -# events_replay.o and dummy/events_modifier.o - ION_DEVICE_SFLAGS = -Iion/src/device/$(MODEL) -Iion/src/device/shared -$(call object_for,$(sort $(ion_device_src) $(dfu_src) $(ion_target_device_flasher_light_src) $(ion_target_device_flasher_verbose_src) $(usb_src) $(ion_target_device_bench_src) $(ion_device_dfu_xip_src) $(ion_device_dfu_relocated_src) $(ion_console_uart_src))): SFLAGS += $(ION_DEVICE_SFLAGS) +$(call object_for,$(ion_device_src) $(ion_device_flasher_src) $(ion_device_bench_src)): SFLAGS += $(ION_DEVICE_SFLAGS) + ion_src += $(ion_device_src) diff --git a/ion/src/device/bench/Makefile b/ion/src/device/bench/Makefile index be49a87f2..523883299 100644 --- a/ion/src/device/bench/Makefile +++ b/ion/src/device/bench/Makefile @@ -1,11 +1,11 @@ -ion_target_device_bench_src += $(addprefix ion/src/device/bench/, \ +ion_device_bench_src += $(addprefix ion/src/device/bench/, \ bench.cpp \ command_handler.cpp \ command_list.cpp \ runner.cpp \ ) -ion_target_device_bench_src += $(addprefix ion/src/device/bench/command/, \ +ion_device_bench_src += $(addprefix ion/src/device/bench/command/, \ adc.cpp \ backlight.cpp \ charge.cpp \ diff --git a/ion/src/device/flasher/Makefile b/ion/src/device/flasher/Makefile index 840b63d2c..c9164faee 100644 --- a/ion/src/device/flasher/Makefile +++ b/ion/src/device/flasher/Makefile @@ -1,9 +1,5 @@ -ion_target_device_flasher_light_src = $(addprefix ion/src/device/flasher/, \ +ion_device_flasher_src = $(addprefix ion/src/device/flasher/, \ main.cpp \ - display_light.cpp \ -) - -ion_target_device_flasher_verbose_src = $(addprefix ion/src/device/flasher/, \ - main.cpp \ - display_verbose.cpp \ + display_light.cpp:+light \ + display_verbose.cpp:-light \ ) diff --git a/ion/src/device/shared/drivers/Makefile b/ion/src/device/shared/drivers/Makefile index ea02cce02..386b519dd 100644 --- a/ion/src/device/shared/drivers/Makefile +++ b/ion/src/device/shared/drivers/Makefile @@ -3,6 +3,8 @@ ion_device_src += $(addprefix ion/src/device/shared/drivers/, \ battery.cpp \ base64.cpp \ board.cpp \ + console.cpp:+consoleuart \ + console_dummy.cpp:-consoleuart \ crc32.cpp \ display.cpp \ events_keyboard_platform.cpp \ @@ -21,7 +23,3 @@ ion_device_src += $(addprefix ion/src/device/shared/drivers/, \ usb.cpp \ wakeup.cpp \ ) - -ion_console_uart_src = ion/src/device/shared/drivers/console.cpp -ion_console_stdio_src += ion/src/device/shared/drivers/console_dummy.cpp -ion_console_display_src += ion/src/device/shared/drivers/console_dummy.cpp diff --git a/ion/src/device/shared/usb/Makefile b/ion/src/device/shared/usb/Makefile index df9d1f6c2..21c46dcbe 100644 --- a/ion/src/device/shared/usb/Makefile +++ b/ion/src/device/shared/usb/Makefile @@ -1,9 +1,11 @@ -usb_src += $(addprefix ion/src/device/shared/usb/, \ +# USB code + +ion_device_usb_src += $(addprefix ion/src/device/shared/usb/, \ calculator.cpp \ dfu_interface.cpp\ ) -usb_src += $(addprefix ion/src/device/shared/usb/stack/, \ +ion_device_usb_src += $(addprefix ion/src/device/shared/usb/stack/, \ device.cpp\ endpoint0.cpp \ interface.cpp\ @@ -12,7 +14,7 @@ usb_src += $(addprefix ion/src/device/shared/usb/stack/, \ streamable.cpp\ ) -usb_src += $(addprefix ion/src/device/shared/usb/stack/descriptor/, \ +ion_device_usb_src += $(addprefix ion/src/device/shared/usb/stack/descriptor/, \ bos_descriptor.cpp\ configuration_descriptor.cpp \ descriptor.cpp\ @@ -29,22 +31,19 @@ usb_src += $(addprefix ion/src/device/shared/usb/stack/descriptor/, \ webusb_platform_descriptor.cpp\ ) -# Sources required to execute DFU in place -ion_device_dfu_xip_src += ion/src/device/shared/usb/dfu_xip.cpp -ion_device_dfu_xip_src += $(usb_src) +# DFU code -# Sources required to execute DFU in RAM -dfu_src += liba/src/assert.c -dfu_src += liba/src/strlen.c -dfu_src += liba/src/strlcpy.c -dfu_src += liba/src/memset.c -dfu_src += liba/src/memcpy.c -dfu_src += libaxx/src/cxxabi/pure_virtual.cpp -dfu_src += ion/src/device/shared/usb/boot.cpp -dfu_src += ion/src/device/$(MODEL)/drivers/board.cpp -dfu_src += ion/src/device/$(MODEL)/drivers/cache.cpp -dfu_src += ion/src/device/$(MODEL)/drivers/reset.cpp -dfu_src += $(addprefix ion/src/device/shared/drivers/, \ +ion_device_dfu_src += liba/src/assert.c +ion_device_dfu_src += liba/src/strlen.c +ion_device_dfu_src += liba/src/strlcpy.c +ion_device_dfu_src += liba/src/memset.c +ion_device_dfu_src += liba/src/memcpy.c +ion_device_dfu_src += libaxx/src/cxxabi/pure_virtual.cpp +ion_device_dfu_src += ion/src/device/shared/usb/boot.cpp +ion_device_dfu_src += ion/src/device/$(MODEL)/drivers/board.cpp +ion_device_dfu_src += ion/src/device/$(MODEL)/drivers/cache.cpp +ion_device_dfu_src += ion/src/device/$(MODEL)/drivers/reset.cpp +ion_device_dfu_src += $(addprefix ion/src/device/shared/drivers/, \ backlight.cpp \ battery.cpp \ base64.cpp \ @@ -68,8 +67,14 @@ dfu_src += $(addprefix ion/src/device/shared/drivers/, \ wakeup.cpp \ ) +# Sources required to execute DFU in place +ion_device_src += ion/src/device/shared/usb/dfu_xip.cpp:+usbxip +ion_device_src += $(addsuffix :+usbxip,$(ion_device_usb_src)) + +# Sources required to execute DFU in RAM + $(BUILD_DIR)/ion/src/device/shared/usb/dfu.elf: LDSCRIPT = ion/src/device/shared/usb/dfu.ld -$(BUILD_DIR)/ion/src/device/shared/usb/dfu.elf: $(call object_for,$(usb_src) $(dfu_src)) +$(BUILD_DIR)/ion/src/device/shared/usb/dfu.elf: $(call object_for,$(ion_device_usb_src) $(ion_device_dfu_src)) # In order to link the dfu bootloader inside the epsilon firmware, we need to # turn the dfu binary (dfu.bin) into an elf object. @@ -83,5 +88,5 @@ $(BUILD_DIR)/ion/src/device/shared/usb/dfu.o: $(BUILD_DIR)/ion/src/device/shared $(call rule_label,OBJCOPY) $(Q) cd $(dir $<) ; $(OBJCOPY) -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata.dfu_bootloader --redefine-sym _binary_dfu_bin_start=_dfu_bootloader_flash_start --redefine-sym _binary_dfu_bin_end=_dfu_bootloader_flash_end $(notdir $<) $(notdir $@) -ion_device_dfu_relocated_src += ion/src/device/shared/usb/dfu.cpp -ion_device_dfu_relocated_src += ion/src/device/shared/usb/dfu_relocated.cpp +ion_device_src += ion/src/device/shared/usb/dfu.cpp:-usbxip +ion_device_src += ion/src/device/shared/usb/dfu_relocated.cpp:-usbxip diff --git a/ion/src/simulator/Makefile b/ion/src/simulator/Makefile index 74ae93666..a0b4645fb 100644 --- a/ion/src/simulator/Makefile +++ b/ion/src/simulator/Makefile @@ -1,9 +1,5 @@ -# TODO ion_src += $(addprefix ion/src/shared/, \ crc32.cpp \ - events.cpp \ - events_keyboard.cpp \ - events_modifier.cpp \ power.cpp \ random.cpp \ timing.cpp \ @@ -18,24 +14,19 @@ ion_src += $(addprefix ion/src/shared/, \ dummy/usb.cpp \ ) -ion_simulator_sdl_src += $(addprefix ion/src/simulator/shared/, \ - display.cpp \ - events_keyboard.cpp \ +ion_src += $(addprefix ion/src/simulator/shared/, \ + console_stdio.cpp:-consoledisplay \ + display.cpp:-headless \ + events_keyboard.cpp:-headless \ + events_stdin.cpp:+headless \ framebuffer_base.cpp \ - keyboard_sdl.cpp \ - main_sdl.cpp \ - layout.cpp \ + framebuffer_png.cpp:+headless \ + keyboard_dummy.cpp:+headless \ + keyboard_sdl.cpp:-headless \ + layout.cpp:-headless \ + main_headless.cpp:+headless \ + main_sdl.cpp:-headless \ ) -ion_simulator_headless_src += $(addprefix ion/src/simulator/shared/, \ - events_stdin.cpp \ - framebuffer_base.cpp \ - framebuffer_png.cpp \ - keyboard_dummy.cpp \ - main_headless.cpp \ -) - -ion_console_stdio_src = ion/src/simulator/shared/console_stdio.cpp - include ion/src/simulator/$(TARGET)/Makefile include ion/src/simulator/external/Makefile From ba57dbde61d907f9a4c8a08e833279f1dad24865 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 2 Apr 2020 21:15:18 -0400 Subject: [PATCH 21/79] [build] macOS and iOS can now build any binary --- build/platform.simulator.ios.mak | 6 ++-- build/platform.simulator.macos.mak | 4 ++- ion/src/simulator/ios/Makefile | 46 ++++++++++++++----------- ion/src/simulator/macos/Makefile | 24 +++++++------ ion/src/simulator/shared/apple/Makefile | 40 +++++++++------------ 5 files changed, 64 insertions(+), 56 deletions(-) diff --git a/build/platform.simulator.ios.mak b/build/platform.simulator.ios.mak index 89a3c3c57..3db1988be 100644 --- a/build/platform.simulator.ios.mak +++ b/build/platform.simulator.ios.mak @@ -1,12 +1,11 @@ TOOLCHAIN = apple -EXE = bin APPLE_PLATFORM ?= ios APPLE_PLATFORM_MIN_VERSION = 8.0 EPSILON_TELEMETRY ?= 1 ifeq ($(APPLE_PLATFORM),ios) -ARCHS ?= arm64 armv7 +ARCHS = arm64 armv7 UI_REQUIRED_CAPABILITIES += armv7 else ifeq ($(APPLE_PLATFORM),ios-simulator) ARCHS = x86_64 @@ -15,5 +14,8 @@ endif BUILD_DIR := $(subst $(TARGET),$(APPLE_PLATFORM),$(BUILD_DIR)) ifdef ARCH +EXE = bin BUILD_DIR := $(BUILD_DIR)/$(ARCH) +else +HANDY_TARGETS_EXTENSIONS = ipa endif diff --git a/build/platform.simulator.macos.mak b/build/platform.simulator.macos.mak index ea7ba551a..09c9f1cea 100644 --- a/build/platform.simulator.macos.mak +++ b/build/platform.simulator.macos.mak @@ -1,5 +1,4 @@ TOOLCHAIN = apple -EXE = bin APPLE_PLATFORM = macos APPLE_PLATFORM_MIN_VERSION = 10.10 @@ -9,5 +8,8 @@ ARCHS = x86_64 EPSILON_SIMULATOR_HAS_LIBPNG = 1 ifdef ARCH +EXE = bin BUILD_DIR := $(BUILD_DIR)/$(ARCH) +else +HANDY_TARGETS_EXTENSIONS = app endif diff --git a/ion/src/simulator/ios/Makefile b/ion/src/simulator/ios/Makefile index 57e1cf41e..d18e5d436 100644 --- a/ion/src/simulator/ios/Makefile +++ b/ion/src/simulator/ios/Makefile @@ -26,6 +26,8 @@ LDFLAGS += -framework CoreData LDFLAGS += -Lion/src/simulator/ios/GoogleAnalyticsServices endif +ifndef ARCH + # App resources SIMULATOR_ICON_SIZES = 20x20 29x29 40x40 40x40 40x40 58x58 58x58 60x60 76x76 \ @@ -35,7 +37,7 @@ SIMULATOR_ICONSET = $(SIMULATOR_ASSETS_PATH)/AppIcon.appiconset include ion/src/simulator/shared/apple/Makefile -$(call simulator_app_plist,%,Info.plist): ion/src/simulator/ios/Info.plist $(call simulator_app_resource,%,Assets.car) +$(call simulator_app_plist,Info.plist): ion/src/simulator/ios/Info.plist $(call simulator_app_resource,Assets.car) $(call rule_label,PLUTIL) $(Q) cp $< $@ $(Q) plutil -insert "BuildMachineOSBuild" -string "$(IOS_BUILD_MACHINE_OS_BUILD)" $@ @@ -54,12 +56,12 @@ $(call simulator_app_plist,%,Info.plist): ion/src/simulator/ios/Info.plist $(cal $(Q) plutil -replace CFBundleIcons -json `plutil -extract CFBundleIcons json -o - $(BUILD_DIR)/app/assets/partial.plist` $@ $(Q) plutil -replace CFBundleIcons~ipad -json `plutil -extract CFBundleIcons~ipad json -o - $(BUILD_DIR)/app/assets/partial.plist` $@ -$(call simulator_app_resource,%,launch.storyboardc): ion/src/simulator/ios/launch.storyboard | $$(@D)/. +$(call simulator_app_resource,launch.storyboardc): ion/src/simulator/ios/launch.storyboard | $$(@D)/. $(call rule_label,IBTOOL) $(Q) $(IBTOOL) --minimum-deployment-target $(APPLE_PLATFORM_MIN_VERSION) --compile $@ $^ ifdef IOS_PROVISIONNING_PROFILE -$(call simulator_app_resource,%,embedded.mobileprovision): $(IOS_PROVISIONNING_PROFILE) | $$(@D)/. +$(call simulator_app_resource,embedded.mobileprovision): $(IOS_PROVISIONNING_PROFILE) | $$(@D)/. $(call rule_label,COPY) $(Q) cp $^ $@ @@ -68,9 +70,9 @@ $(BUILD_DIR)/app/entitlements.plist: $(IOS_PROVISIONNING_PROFILE) $(Q) security cms -D -i $(IOS_PROVISIONNING_PROFILE) | plutil -extract Entitlements xml1 - -o $@ simulator_app_deps += $(BUILD_DIR)/app/entitlements.plist -simulator_app_deps += $(call simulator_app_resource,$1,embedded.mobileprovision) +simulator_app_deps += $(call simulator_app_resource,embedded.mobileprovision) else -$(call simulator_app_resource,%,embedded.mobileprovision): +$(call simulator_app_resource,embedded.mobileprovision): $(warning Building without a provisionning profile. Please define IOS_PROVISIONNING_PROFILE to point to the .mobileprovision file you want to use.) endif @@ -78,29 +80,33 @@ $(SIMULATOR_ICONSET)/Contents.json: ion/src/simulator/ios/icon_assets.json $(SIM $(call rule_label,COPY) $(Q) cp $< $@ -$(call simulator_app_resource,%,Assets.car): $(SIMULATOR_ICONSET)/Contents.json | $$(@D)/. +$(call simulator_app_resource,Assets.car): $(SIMULATOR_ICONSET)/Contents.json | $$(@D)/. $(call rule_label,ACTOOL) - $(Q) $(ACTOOL) --compile $(BUILD_DIR)/app/Payload/$*.app --minimum-deployment-target $(APPLE_PLATFORM_MIN_VERSION) --platform $(APPLE_SDK) --app-icon AppIcon --output-partial-info-plist $(BUILD_DIR)/app/assets/partial.plist $(SIMULATOR_ASSETS_PATH) > /dev/null + $(Q) $(ACTOOL) --compile $(BUILD_DIR)/$*.app --minimum-deployment-target $(APPLE_PLATFORM_MIN_VERSION) --platform $(APPLE_SDK) --app-icon AppIcon --output-partial-info-plist $(BUILD_DIR)/app/assets/partial.plist $(SIMULATOR_ASSETS_PATH) > /dev/null -simulator_app_deps += $(call simulator_app_resource,$(1), \ - Assets.car \ - launch.storyboardc \ -) +simulator_app_deps += $(call simulator_app_resource,Assets.car) +simulator_app_deps += $(call simulator_app_resource,launch.storyboardc) -$(BUILD_DIR)/app/epsilon%ipa: $$(subst ..,.,$$(call simulator_app_deps,Epsilon$$*)) +.PRECIOUS: $(simulator_app_deps) + +.PRECIOUS: $(BUILD_DIR)/%.app +.PHONY: $(BUILD_DIR)/%.app +$(BUILD_DIR)/%.app: $(simulator_app_deps) + +$(BUILD_DIR)/%.ipa: $(BUILD_DIR)/%.app ifdef IOS_PROVISIONNING_PROFILE $(call rule_label,SIGN) - $(Q) codesign --force --entitlements $(BUILD_DIR)/app/entitlements.plist --sign "Apple Distribution: NumWorks" $(BUILD_DIR)/app/Payload/Epsilon$*app + $(Q) codesign --force --entitlements $(BUILD_DIR)/app/entitlements.plist --sign "Apple Distribution: NumWorks" $(BUILD_DIR)/$*.app endif $(call rule_label,ZIP) - $(Q) cd $(dir $@) ; zip -qr9 $(notdir $@) Payload + $(Q) cd $(dir $@) ; zip -qr9 $(notdir $@) $*.app -ifndef ARCH -DEFAULT := $(BUILD_DIR)/app/epsilon.ipa -endif +DEFAULT = epsilon.ipa ifeq ($(APPLE_PLATFORM),ios-simulator) -.PHONY: epsilon%run -epsilon%run: $(subst _.,.,$(call simulator_app_deps,Epsilon$*)) - xcrun simctl install booted $(BUILD_DIR)/app/Payload/Epsilon$(subst _,.,$*)app +.PHONY: %_run +%_run: $(BUILD_DIR)/%.app + xcrun simctl install booted $^ +endif + endif diff --git a/ion/src/simulator/macos/Makefile b/ion/src/simulator/macos/Makefile index 0f27650df..43988e2fb 100644 --- a/ion/src/simulator/macos/Makefile +++ b/ion/src/simulator/macos/Makefile @@ -12,6 +12,8 @@ ion_src += ion/src/simulator/shared/dummy/telemetry_init.cpp ion_src += ion/src/shared/telemetry_console.cpp endif +ifndef ARCH + # App resources SIMULATOR_ICON_SIZES = 16x16 32x32 64x64 128x128 256x256 512x512 1024x1024 @@ -22,7 +24,7 @@ SIMULATOR_ICONSET = $(BUILD_DIR)/app/assets/app.iconset include ion/src/simulator/shared/apple/Makefile -$(call simulator_app_plist,%,Info.plist): ion/src/simulator/macos/Info.plist +$(call simulator_app_plist,Info.plist): ion/src/simulator/macos/Info.plist | $$(@D)/. $(call rule_label,PLUTIL) $(Q) cp $< $@ $(Q) plutil -insert "LSMinimumSystemVersion" -string "$(MACOS_MIN_VERSION)" $@ @@ -33,19 +35,21 @@ $(call simulator_app_plist,%,Info.plist): ion/src/simulator/macos/Info.plist .SECONDARY: $(SIMULATOR_ICONS) | $$(@D)/. -$(call simulator_app_resource,%,app.icns): $(SIMULATOR_ICONS) | $$(@D)/. +$(call simulator_app_resource,app.icns): $(SIMULATOR_ICONS) | $$(@D)/. $(call rule_label,ICNUTIL) $(Q) iconutil --convert icns --output $@ $(SIMULATOR_ICONSET) -simulator_app_deps += $(call simulator_app_resource,$(1),app.icns) +simulator_app_deps += $(call simulator_app_resource,app.icns) -simulator_app_deps_unofficial = $(call simulator_app_deps,Epsilon) -simulator_app_deps_official = $(call simulator_app_deps,Epsilon.official) +.PRECIOUS: $(simulator_app_deps) -.PHONY: Epsilon.app Epsilon.official.app -Epsilon.app: $(simulator_app_deps_unofficial) -Epsilon.official.app: $(simulator_app_deps_official) +$(BUILD_DIR)/%.app: $(simulator_app_deps) + $(Q) echo "DONE" + +#.PHONY: Epsilon.app Epsilon.official.app +#Epsilon.app: $(simulator_app_deps_unofficial) +#Epsilon.official.app: $(simulator_app_deps_official) + +DEFAULT = epsilon.app -ifndef ARCH -DEFAULT := Epsilon.app endif diff --git a/ion/src/simulator/shared/apple/Makefile b/ion/src/simulator/shared/apple/Makefile index 22faf47d7..062862496 100644 --- a/ion/src/simulator/shared/apple/Makefile +++ b/ion/src/simulator/shared/apple/Makefile @@ -2,37 +2,31 @@ # The only things that have to be customized per platform are the icons and the # Info.plist. -SIMULATOR_APP_PATH = $(BUILD_DIR)/app/Payload +SIMULATOR_APP_PATH = $(BUILD_DIR) -simulator_app_binary = $(addprefix $(SIMULATOR_APP_PATH)/$(1).app/$(SIMULATOR_APP_BINARY_PATH),$(2)) -simulator_app_resource = $(addprefix $(SIMULATOR_APP_PATH)/$(1).app/$(SIMULATOR_APP_RESOURCE_PATH),$(2)) -simulator_app_plist = $(addprefix $(SIMULATOR_APP_PATH)/$(1).app/$(SIMULATOR_APP_PLIST_PATH),$(2)) +simulator_app_binary = $(SIMULATOR_APP_PATH)/%.app/$(SIMULATOR_APP_BINARY_PATH)Epsilon +simulator_app_resource = $(SIMULATOR_APP_PATH)/%.app/$(SIMULATOR_APP_RESOURCE_PATH)$(1) +simulator_app_plist = $(SIMULATOR_APP_PATH)/%.app/$(SIMULATOR_APP_PLIST_PATH)$(1) # Epsilon binary .PHONY: force_remake - -define rule_for_epsilon -$$(BUILD_DIR)/%/$(1): force_remake - $(Q) echo "MAKE ARCH=$$*" - $(Q) $$(MAKE) ARCH=$$* $(1) +define rule_for_arch_executable +.PRECIOUS: $$(BUILD_DIR)/$(1)/%.bin +$$(BUILD_DIR)/$(1)/%.bin: force_remake + $(Q) echo "MAKE ARCH=$(1)" + $(Q) $$(MAKE) ARCH=$(1) --silent $$*.bin endef -$(eval $(call rule_for_epsilon,epsilon.bin)) -$(eval $(call rule_for_epsilon,epsilon.official.bin)) +$(foreach ARCH,$(ARCHS),$(eval $(call rule_for_arch_executable,$(ARCH)))) -define rule_for_lipo -$$(call simulator_app_binary,$1,Epsilon): $$(patsubst %,$(BUILD_DIR)/%/$2.bin,$$(ARCHS)) | $$$$(@D)/. - $$(call rule_label,LIPO) - $(Q) $$(LIPO) -create $$^ -output $$@ -endef - -$(eval $(call rule_for_lipo,Epsilon,epsilon)) -$(eval $(call rule_for_lipo,Epsilon.official,epsilon.official)) +$(simulator_app_binary): $(foreach arch,$(ARCHS),$(BUILD_DIR)/$(arch)/%.bin) | $$(@D)/. + $(call rule_label,LIPO) + $(Q) $(LIPO) -create $^ -output $@ # Background image -$(call simulator_app_resource,%,background.jpg): ion/src/simulator/assets/background.jpg | $$(@D)/. +$(call simulator_app_resource,background.jpg): ion/src/simulator/assets/background.jpg | $$(@D)/. $(call rule_label,COPY) $(Q) cp $^ $@ @@ -54,6 +48,6 @@ $(addprefix $(SIMULATOR_ICONSET)/,icon_%.png): ion/src/simulator/assets/logo.svg # Export simulator app dependencies -simulator_app_deps += $(call simulator_app_binary,$(1),Epsilon) -simulator_app_deps += $(call simulator_app_plist,$(1),Info.plist) -simulator_app_deps += $(call simulator_app_resource,$(1),background.jpg) +simulator_app_deps += $(simulator_app_binary) +simulator_app_deps += $(call simulator_app_plist,Info.plist) +simulator_app_deps += $(call simulator_app_resource,background.jpg) From 3f2b14ac3e9376ff463868b7107bffd9c7a86da1 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 2 Apr 2020 21:26:22 -0400 Subject: [PATCH 22/79] [build] Misc cleanups --- Makefile | 5 ----- ion/src/simulator/android/Makefile | 1 - 2 files changed, 6 deletions(-) diff --git a/Makefile b/Makefile index c416c3133..b2119d461 100644 --- a/Makefile +++ b/Makefile @@ -11,11 +11,6 @@ include build/toolchain.$(TOOLCHAIN).mak include build/variants.mak include build/helpers.mk -define foo -endef - -$(eval $(call foo)) - .PHONY: info info: @echo "EPSILON_VERSION = $(EPSILON_VERSION)" diff --git a/ion/src/simulator/android/Makefile b/ion/src/simulator/android/Makefile index a417ca66b..0d0410e1e 100644 --- a/ion/src/simulator/android/Makefile +++ b/ion/src/simulator/android/Makefile @@ -15,7 +15,6 @@ $(call object_for,ion/src/simulator/shared/main.cpp) : SFLAGS += -DEPSILON_SDL_F LDFLAGS += -ljnigraphics -llog - # If ARCH is not defined, we will re-trigger a build for each avaialble ARCH. # This is used to build APKs, which needs to embbed a binary for each ARCH. From ed7038262c2a6c48a168bc1eb1dad86f501f4e4c Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 2 Apr 2020 21:26:42 -0400 Subject: [PATCH 23/79] [ci] Misc cleanups --- .github/workflows/ci-workflow.yml | 57 +++++++++++++++++-------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index ce71e4b32..8a49fc7f0 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -7,10 +7,12 @@ jobs: steps: - uses: actions/checkout@v2 - run: make -j2 PLATFORM=simulator TARGET=android + - run: make -j2 PLATFORM=simulator TARGET=android epsilon.official.apk + - run: make -j2 PLATFORM=simulator TARGET=android test.apk - uses: actions/upload-artifact@master with: - name: epsilon-simulator-android.apk - path: output/release/simulator/android/app/outputs/apk/release/android-release-unsigned.apk + name: epsilon-android.apk + path: output/release/simulator/android/epsilon.apk build-device-n0100: runs-on: ubuntu-latest steps: @@ -24,11 +26,11 @@ jobs: - run: make -j2 MODEL=n0100 epsilon.onboarding.beta.dfu - run: make -j2 MODEL=n0100 flasher.light.dfu - run: make -j2 MODEL=n0100 flasher.verbose.dfu + - run: make -j2 MODEL=n0100 test.elf - uses: actions/upload-artifact@master with: - name: epsilon-device-n0100.dfu + name: epsilon-n0100.dfu path: output/release/device/n0100/epsilon.dfu - - run: make -j2 MODEL=n0100 test.elf build-device-n0110: runs-on: ubuntu-latest steps: @@ -44,11 +46,11 @@ jobs: - run: make -j2 flasher.verbose.dfu - run: make -j2 bench.ram.dfu - run: make -j2 bench.flash.dfu + - run: make -j2 test.elf - uses: actions/upload-artifact@master with: - name: epsilon-device-n0110.dfu + name: epsilon-n0110.dfu path: output/release/device/n0110/epsilon.dfu - - run: make -j2 test.elf build-simulator-windows: runs-on: windows-latest steps: @@ -56,12 +58,13 @@ jobs: - uses: actions/checkout@v2 - run: msys2do pacman -S --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-freetype mingw-w64-x86_64-pkg-config make mingw-w64-x86_64-python3 mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-libpng - run: msys2do make -j2 PLATFORM=simulator - - uses: actions/upload-artifact@master - with: - name: epsilon-simulator-windows.exe - path: output/release/simulator/windows/epsilon.exe + - run: msys2do make -j2 PLATFORM=simulator epsilon.official.exe - run: msys2do make -j2 PLATFORM=simulator test.headless.exe - run: output\release\simulator\windows\test.headless.exe + - uses: actions/upload-artifact@master + with: + name: epsilon-windows.exe + path: output/release/simulator/windows/epsilon.exe build-simulator-web: runs-on: ubuntu-latest steps: @@ -70,47 +73,51 @@ jobs: sdk: latest-fastcomp - uses: actions/checkout@v2 - run: make -j2 PLATFORM=simulator TARGET=web - - run: make -j2 PLATFORM=simulator TARGET=web epsilon.official.js - - uses: actions/upload-artifact@master - with: - name: epsilon-simulator-web.zip - path: output/release/simulator/web/epsilon.zip + - run: make -j2 PLATFORM=simulator TARGET=web epsilon.official.zip - run: make -j2 PLATFORM=simulator TARGET=web test.headless.js - run: node output/release/simulator/web/test.headless.js + - uses: actions/upload-artifact@master + with: + name: epsilon-web.zip + path: output/release/simulator/web/epsilon.zip build-simulator-linux: runs-on: ubuntu-latest steps: - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config - uses: actions/checkout@v2 - run: make -j2 PLATFORM=simulator - - uses: actions/upload-artifact@master - with: - name: epsilon-simulator-linux.bin - path: output/release/simulator/linux/epsilon.bin + - run: make -j2 PLATFORM=simulator epsilon.official.bin - run: make -j2 PLATFORM=simulator test.headless.bin - run: output/release/simulator/linux/test.headless.bin + - uses: actions/upload-artifact@master + with: + name: epsilon-linux.bin + path: output/release/simulator/linux/epsilon.bin build-simulator-macos: runs-on: macOS-latest steps: - run: brew install numworks/tap/epsilon-sdk - uses: actions/checkout@v2 - run: make -j2 PLATFORM=simulator - - uses: actions/upload-artifact@master - with: - name: epsilon-simulator-macos.zip - path: output/release/simulator/macos/app/Payload + - run: make -j2 PLATFORM=simulator epsilon.official.app - run: make -j2 PLATFORM=simulator ARCH=x86_64 test.headless.bin - run: output/release/simulator/macos/x86_64/test.headless.bin + - uses: actions/upload-artifact@master + with: + name: epsilon-macos.zip + path: output/release/simulator/macos/epsilon.app build-simulator-ios: runs-on: macOS-latest steps: - run: brew install numworks/tap/epsilon-sdk - uses: actions/checkout@v2 - run: make -j2 PLATFORM=simulator TARGET=ios EPSILON_TELEMETRY=0 + - run: make -j2 PLATFORM=simulator TARGET=ios EPSILON_TELEMETRY=0 epsilon.official.ipa + - run: make -j2 PLATFORM=simulator TARGET=ios EPSILON_TELEMETRY=0 test.ipa - uses: actions/upload-artifact@master with: - name: epsilon-simulator-ios.ipa - path: output/release/simulator/ios/app/epsilon.ipa + name: epsilon-ios.ipa + path: output/release/simulator/ios/epsilon.ipa env: ACCEPT_OFFICIAL_TOS: 1 From d4149b1a3db42e4fcef534d3358e89f4678d9f97 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 2 Apr 2020 21:34:30 -0400 Subject: [PATCH 24/79] [build] Move %_run targets in the build dir --- build/targets.simulator.android.mak | 5 +++++ build/targets.simulator.ios.mak | 10 ++++++++++ ion/src/simulator/android/Makefile | 7 +------ ion/src/simulator/ios/Makefile | 6 ------ 4 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 build/targets.simulator.ios.mak diff --git a/build/targets.simulator.android.mak b/build/targets.simulator.android.mak index e6f2d9dbc..1cd826d3c 100644 --- a/build/targets.simulator.android.mak +++ b/build/targets.simulator.android.mak @@ -1,3 +1,8 @@ ifndef ARCH HANDY_TARGETS_EXTENSIONS += apk endif + +.PHONY: %_run +%_run: $(BUILD_DIR)/%.apk + $(call rule_label,ADB) + $(Q) adb install $< diff --git a/build/targets.simulator.ios.mak b/build/targets.simulator.ios.mak new file mode 100644 index 000000000..c5f26ad8d --- /dev/null +++ b/build/targets.simulator.ios.mak @@ -0,0 +1,10 @@ +ifndef ARCH +HANDY_TARGETS_EXTENSIONS += apk +endif + +ifeq ($(APPLE_PLATFORM),ios-simulator) +.PHONY: %_run +%_run: $(BUILD_DIR)/%.app + $(call rule_label,XCRUN) + $(Q) xcrun simctl install booted $^ +endif diff --git a/ion/src/simulator/android/Makefile b/ion/src/simulator/android/Makefile index 0d0410e1e..a0417ebd9 100644 --- a/ion/src/simulator/android/Makefile +++ b/ion/src/simulator/android/Makefile @@ -71,15 +71,10 @@ apk_deps += $(addprefix $(BUILD_DIR)/app/res/,mipmap/ic_launcher.png mipmap-v26/ .PRECIOUS: $(apk_deps) $(BUILD_DIR)/%.apk: $(apk_deps) - @echo "GRADLE ion/src/simulator/android/build.gradle" + $(call rule_label,GRADLE) $(Q) ANDROID_HOME=$(ANDROID_HOME) EPSILON_VERSION=$(EPSILON_VERSION) BUILD_DIR=$(BUILD_DIR) EPSILON_VARIANT=$* ion/src/simulator/android/gradlew -b ion/src/simulator/android/build.gradle assembleRelease $(Q) cp $(BUILD_DIR)/app/outputs/apk/release/android-release*.apk $@ DEFAULT = epsilon.apk -.PHONY: %_run -%_run: $(BUILD_DIR)/%.apk - @echo "ADB $*.apk" - $(Q) adb install $< - endif diff --git a/ion/src/simulator/ios/Makefile b/ion/src/simulator/ios/Makefile index d18e5d436..a9dca15e5 100644 --- a/ion/src/simulator/ios/Makefile +++ b/ion/src/simulator/ios/Makefile @@ -103,10 +103,4 @@ endif DEFAULT = epsilon.ipa -ifeq ($(APPLE_PLATFORM),ios-simulator) -.PHONY: %_run -%_run: $(BUILD_DIR)/%.app - xcrun simctl install booted $^ -endif - endif From fff20eb530512b29dbc821317a0056d0f808d8c6 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 2 Apr 2020 21:59:27 -0400 Subject: [PATCH 25/79] [ion/simulator/apple] Rename Make helper --- ion/src/simulator/ios/Makefile | 2 +- ion/src/simulator/macos/Makefile | 2 +- ion/src/simulator/shared/apple/{Makefile => app.mak} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename ion/src/simulator/shared/apple/{Makefile => app.mak} (100%) diff --git a/ion/src/simulator/ios/Makefile b/ion/src/simulator/ios/Makefile index a9dca15e5..64b254df4 100644 --- a/ion/src/simulator/ios/Makefile +++ b/ion/src/simulator/ios/Makefile @@ -35,7 +35,7 @@ SIMULATOR_ICON_SIZES = 20x20 29x29 40x40 40x40 40x40 58x58 58x58 60x60 76x76 \ SIMULATOR_ASSETS_PATH = $(BUILD_DIR)/app/assets/Assets.xcassets SIMULATOR_ICONSET = $(SIMULATOR_ASSETS_PATH)/AppIcon.appiconset -include ion/src/simulator/shared/apple/Makefile +include ion/src/simulator/shared/apple/app.mak $(call simulator_app_plist,Info.plist): ion/src/simulator/ios/Info.plist $(call simulator_app_resource,Assets.car) $(call rule_label,PLUTIL) diff --git a/ion/src/simulator/macos/Makefile b/ion/src/simulator/macos/Makefile index 43988e2fb..4c6cb4900 100644 --- a/ion/src/simulator/macos/Makefile +++ b/ion/src/simulator/macos/Makefile @@ -22,7 +22,7 @@ SIMULATOR_APP_RESOURCE_PATH = Contents/Resources/ SIMULATOR_APP_PLIST_PATH = Contents/ SIMULATOR_ICONSET = $(BUILD_DIR)/app/assets/app.iconset -include ion/src/simulator/shared/apple/Makefile +include ion/src/simulator/shared/apple/app.mak $(call simulator_app_plist,Info.plist): ion/src/simulator/macos/Info.plist | $$(@D)/. $(call rule_label,PLUTIL) diff --git a/ion/src/simulator/shared/apple/Makefile b/ion/src/simulator/shared/apple/app.mak similarity index 100% rename from ion/src/simulator/shared/apple/Makefile rename to ion/src/simulator/shared/apple/app.mak From f9b08435789319e6a02339b668e96e46d0eb974d Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 2 Apr 2020 22:24:55 -0400 Subject: [PATCH 26/79] [build/simulator/apple] Share code --- ion/src/simulator/ios/Makefile | 16 ++-------------- ion/src/simulator/macos/Makefile | 11 ++--------- .../shared/apple/{app.mak => helpers.mak} | 10 ---------- ion/src/simulator/shared/apple/targets.mak | 16 ++++++++++++++++ 4 files changed, 20 insertions(+), 33 deletions(-) rename ion/src/simulator/shared/apple/{app.mak => helpers.mak} (83%) create mode 100644 ion/src/simulator/shared/apple/targets.mak diff --git a/ion/src/simulator/ios/Makefile b/ion/src/simulator/ios/Makefile index 64b254df4..a8b0c36d6 100644 --- a/ion/src/simulator/ios/Makefile +++ b/ion/src/simulator/ios/Makefile @@ -35,7 +35,7 @@ SIMULATOR_ICON_SIZES = 20x20 29x29 40x40 40x40 40x40 58x58 58x58 60x60 76x76 \ SIMULATOR_ASSETS_PATH = $(BUILD_DIR)/app/assets/Assets.xcassets SIMULATOR_ICONSET = $(SIMULATOR_ASSETS_PATH)/AppIcon.appiconset -include ion/src/simulator/shared/apple/app.mak +include ion/src/simulator/shared/apple/helpers.mak $(call simulator_app_plist,Info.plist): ion/src/simulator/ios/Info.plist $(call simulator_app_resource,Assets.car) $(call rule_label,PLUTIL) @@ -87,19 +87,7 @@ $(call simulator_app_resource,Assets.car): $(SIMULATOR_ICONSET)/Contents.json | simulator_app_deps += $(call simulator_app_resource,Assets.car) simulator_app_deps += $(call simulator_app_resource,launch.storyboardc) -.PRECIOUS: $(simulator_app_deps) - -.PRECIOUS: $(BUILD_DIR)/%.app -.PHONY: $(BUILD_DIR)/%.app -$(BUILD_DIR)/%.app: $(simulator_app_deps) - -$(BUILD_DIR)/%.ipa: $(BUILD_DIR)/%.app -ifdef IOS_PROVISIONNING_PROFILE - $(call rule_label,SIGN) - $(Q) codesign --force --entitlements $(BUILD_DIR)/app/entitlements.plist --sign "Apple Distribution: NumWorks" $(BUILD_DIR)/$*.app -endif - $(call rule_label,ZIP) - $(Q) cd $(dir $@) ; zip -qr9 $(notdir $@) $*.app +include ion/src/simulator/shared/apple/targets.mak DEFAULT = epsilon.ipa diff --git a/ion/src/simulator/macos/Makefile b/ion/src/simulator/macos/Makefile index 4c6cb4900..3992f08fa 100644 --- a/ion/src/simulator/macos/Makefile +++ b/ion/src/simulator/macos/Makefile @@ -22,7 +22,7 @@ SIMULATOR_APP_RESOURCE_PATH = Contents/Resources/ SIMULATOR_APP_PLIST_PATH = Contents/ SIMULATOR_ICONSET = $(BUILD_DIR)/app/assets/app.iconset -include ion/src/simulator/shared/apple/app.mak +include ion/src/simulator/shared/apple/helpers.mak $(call simulator_app_plist,Info.plist): ion/src/simulator/macos/Info.plist | $$(@D)/. $(call rule_label,PLUTIL) @@ -41,14 +41,7 @@ $(call simulator_app_resource,app.icns): $(SIMULATOR_ICONS) | $$(@D)/. simulator_app_deps += $(call simulator_app_resource,app.icns) -.PRECIOUS: $(simulator_app_deps) - -$(BUILD_DIR)/%.app: $(simulator_app_deps) - $(Q) echo "DONE" - -#.PHONY: Epsilon.app Epsilon.official.app -#Epsilon.app: $(simulator_app_deps_unofficial) -#Epsilon.official.app: $(simulator_app_deps_official) +include ion/src/simulator/shared/apple/targets.mak DEFAULT = epsilon.app diff --git a/ion/src/simulator/shared/apple/app.mak b/ion/src/simulator/shared/apple/helpers.mak similarity index 83% rename from ion/src/simulator/shared/apple/app.mak rename to ion/src/simulator/shared/apple/helpers.mak index 062862496..dbbf8fc17 100644 --- a/ion/src/simulator/shared/apple/app.mak +++ b/ion/src/simulator/shared/apple/helpers.mak @@ -10,16 +10,6 @@ simulator_app_plist = $(SIMULATOR_APP_PATH)/%.app/$(SIMULATOR_APP_PLIST_PATH)$(1 # Epsilon binary -.PHONY: force_remake -define rule_for_arch_executable -.PRECIOUS: $$(BUILD_DIR)/$(1)/%.bin -$$(BUILD_DIR)/$(1)/%.bin: force_remake - $(Q) echo "MAKE ARCH=$(1)" - $(Q) $$(MAKE) ARCH=$(1) --silent $$*.bin -endef - -$(foreach ARCH,$(ARCHS),$(eval $(call rule_for_arch_executable,$(ARCH)))) - $(simulator_app_binary): $(foreach arch,$(ARCHS),$(BUILD_DIR)/$(arch)/%.bin) | $$(@D)/. $(call rule_label,LIPO) $(Q) $(LIPO) -create $^ -output $@ diff --git a/ion/src/simulator/shared/apple/targets.mak b/ion/src/simulator/shared/apple/targets.mak new file mode 100644 index 000000000..0245e6fb6 --- /dev/null +++ b/ion/src/simulator/shared/apple/targets.mak @@ -0,0 +1,16 @@ +.PRECIOUS: $(simulator_app_deps) + +# CAUTION: The empty line in this rule is important! +# Indeed, rule without receipe serve a special purpose +# https://www.gnu.org/software/make/manual/html_node/Canceling-Rules.html +.PRECIOUS: $(BUILD_DIR)/%.app +$(BUILD_DIR)/%.app: $(simulator_app_deps) + + +$(BUILD_DIR)/%.ipa: $(BUILD_DIR)/%.app +ifdef IOS_PROVISIONNING_PROFILE + $(call rule_label,SIGN) + $(Q) codesign --force --entitlements $(BUILD_DIR)/app/entitlements.plist --sign "Apple Distribution: NumWorks" $(BUILD_DIR)/$*.app +endif + $(call rule_label,ZIP) + $(Q) cd $(dir $@) ; zip -qr9 $(notdir $@) $*.app From 99b916e179ec027b1a73d66d7c8bd085e233404f Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 2 Apr 2020 22:37:34 -0400 Subject: [PATCH 27/79] [build] Share multi-app code --- build/helpers.mk | 19 +++++++++++++++++++ build/platform.simulator.android.mak | 4 +++- build/platform.simulator.ios.mak | 4 ++-- build/platform.simulator.macos.mak | 2 +- ion/src/simulator/android/Makefile | 12 ------------ 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/build/helpers.mk b/build/helpers.mk index f0b68cf28..b397ea96a 100644 --- a/build/helpers.mk +++ b/build/helpers.mk @@ -34,3 +34,22 @@ endef define object_for $(call direct_object_for,$(call any_variant,$(1))) endef + +# Multi-arch helpers +ifdef ARCHS +ifndef ARCH + +# This rule allow us to build any executable (%) for a specified ARCH ($1) +# We depend on a phony target to make sure this rule is always executed +.PHONY: force_remake +define rule_for_arch_executable +.PRECIOUS: $$(BUILD_DIR)/$(1)/%.$$(EXE) +$$(BUILD_DIR)/$(1)/%.$$(EXE): force_remake + $(Q) echo "MAKE ARCH=$(1)" + $(Q) $$(MAKE) ARCH=$(1) --silent $$*.$$(EXE) +endef + +$(foreach ARCH,$(ARCHS),$(eval $(call rule_for_arch_executable,$(ARCH)))) + +endif +endif diff --git a/build/platform.simulator.android.mak b/build/platform.simulator.android.mak index 46601bfea..d8f8f2199 100644 --- a/build/platform.simulator.android.mak +++ b/build/platform.simulator.android.mak @@ -1,10 +1,12 @@ TOOLCHAIN = android +EXE = so EPSILON_TELEMETRY ?= 1 ARCHS = armeabi-v7a arm64-v8a x86 x86_64 ifdef ARCH -EXE = so BUILD_DIR := $(BUILD_DIR)/$(ARCH) +else +HANDY_TARGETS_EXTENSIONS = apk endif diff --git a/build/platform.simulator.ios.mak b/build/platform.simulator.ios.mak index 3db1988be..b53ad508a 100644 --- a/build/platform.simulator.ios.mak +++ b/build/platform.simulator.ios.mak @@ -1,4 +1,5 @@ TOOLCHAIN = apple +EXE = bin APPLE_PLATFORM ?= ios APPLE_PLATFORM_MIN_VERSION = 8.0 @@ -14,8 +15,7 @@ endif BUILD_DIR := $(subst $(TARGET),$(APPLE_PLATFORM),$(BUILD_DIR)) ifdef ARCH -EXE = bin BUILD_DIR := $(BUILD_DIR)/$(ARCH) else -HANDY_TARGETS_EXTENSIONS = ipa +HANDY_TARGETS_EXTENSIONS = ipa app endif diff --git a/build/platform.simulator.macos.mak b/build/platform.simulator.macos.mak index 09c9f1cea..5f2c492e5 100644 --- a/build/platform.simulator.macos.mak +++ b/build/platform.simulator.macos.mak @@ -1,4 +1,5 @@ TOOLCHAIN = apple +EXE = bin APPLE_PLATFORM = macos APPLE_PLATFORM_MIN_VERSION = 10.10 @@ -8,7 +9,6 @@ ARCHS = x86_64 EPSILON_SIMULATOR_HAS_LIBPNG = 1 ifdef ARCH -EXE = bin BUILD_DIR := $(BUILD_DIR)/$(ARCH) else HANDY_TARGETS_EXTENSIONS = app diff --git a/ion/src/simulator/android/Makefile b/ion/src/simulator/android/Makefile index a0417ebd9..c3f31d5f1 100644 --- a/ion/src/simulator/android/Makefile +++ b/ion/src/simulator/android/Makefile @@ -36,17 +36,6 @@ $(BUILD_DIR)/app/res/%.xml: ion/src/simulator/android/src/res/%.xml | $$(@D)/. $(call rule_label,COPY) $(Q) cp $< $@ -# This rule allow us to build any executable (%) for a specified ARCH ($1) -# We depend on a phony target to make sure this rule is always executed - -.PHONY: force_remake -define rule_for_arch_executable -.PRECIOUS: $$(BUILD_DIR)/$(1)/%.so -$$(BUILD_DIR)/$(1)/%.so: force_remake - $(Q) echo "MAKE ARCH=$(1) $$*.so" - $(Q) $$(MAKE) ARCH=$(1) --silent $$*.so -endef - # We need to put the .so files somewhere Gradle can pick them up. # We decided to use the location "app/libs/$EXECUTABLE/$ARCH/libepsilon.so" # This way it's easy to import the shared object from Java code (it's always @@ -61,7 +50,6 @@ $(call path_for_arch_jni_lib,$(1)): $$(BUILD_DIR)/$(1)/%.so | $$$$(@D)/. $(Q) cp $$< $$@ endef -$(foreach ARCH,$(ARCHS),$(eval $(call rule_for_arch_executable,$(ARCH)))) $(foreach ARCH,$(ARCHS),$(eval $(call rule_for_arch_jni_lib,$(ARCH)))) apk_deps = $(foreach ARCH,$(ARCHS),$(call path_for_arch_jni_lib,$(ARCH))) From f7911bae727f01fb667aee351895699ea714780e Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 2 Apr 2020 22:44:22 -0400 Subject: [PATCH 28/79] [build] Remove DEFAULT It was redundant with HANDY_TARGETS and HANDY_TARGETS_EXTENSIONS --- Makefile | 3 +-- build/targets.mak | 20 +++++++++++++------- ion/src/simulator/android/Makefile | 2 -- ion/src/simulator/ios/Makefile | 2 -- ion/src/simulator/macos/Makefile | 2 -- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index b2119d461..c5ed4b53d 100644 --- a/Makefile +++ b/Makefile @@ -99,8 +99,7 @@ all_objs = $(call object_for,$(all_src)) include build/targets.mak # Fill in the default recipe -DEFAULT ?= $(BUILD_DIR)/epsilon.$(EXE) -default: $(DEFAULT) +default: $(firstword $(HANDY_TARGETS)).$(firstword $(HANDY_TARGETS_EXTENSIONS)) # Load standard build rules include build/rules.mk diff --git a/build/targets.mak b/build/targets.mak index cf77a3460..85303eb5d 100644 --- a/build/targets.mak +++ b/build/targets.mak @@ -2,13 +2,8 @@ # Those can be built easily by simply invoking "make target.ext". The named file # will be built in $(BUILD_DIR). -HANDY_TARGETS += -HANDY_TARGETS_EXTENSIONS += $(EXE) - -define handy_target_rule -.PHONY: $(1).$(2) -$(1).$(2): $$(BUILD_DIR)/$(1).$(2) -endef +HANDY_TARGETS ?= +HANDY_TARGETS_EXTENSIONS ?= # Epsilon base target @@ -74,6 +69,17 @@ HANDY_TARGETS += test -include build/targets.$(PLATFORM).mak # Generate handy targets rules +# Define handy targets +# Those can be built easily by simply invoking "make target.ext". The named file +# will be built in $(BUILD_DIR). + +HANDY_TARGETS_EXTENSIONS += $(EXE) + +define handy_target_rule +.PHONY: $(1).$(2) +$(1).$(2): $$(BUILD_DIR)/$(1).$(2) +endef + $(foreach extension,$(HANDY_TARGETS_EXTENSIONS),$(foreach executable,$(HANDY_TARGETS),$(eval $(call handy_target_rule,$(executable),$(extension))))) include build/targets.all.mak diff --git a/ion/src/simulator/android/Makefile b/ion/src/simulator/android/Makefile index c3f31d5f1..9143b3dfe 100644 --- a/ion/src/simulator/android/Makefile +++ b/ion/src/simulator/android/Makefile @@ -63,6 +63,4 @@ $(BUILD_DIR)/%.apk: $(apk_deps) $(Q) ANDROID_HOME=$(ANDROID_HOME) EPSILON_VERSION=$(EPSILON_VERSION) BUILD_DIR=$(BUILD_DIR) EPSILON_VARIANT=$* ion/src/simulator/android/gradlew -b ion/src/simulator/android/build.gradle assembleRelease $(Q) cp $(BUILD_DIR)/app/outputs/apk/release/android-release*.apk $@ -DEFAULT = epsilon.apk - endif diff --git a/ion/src/simulator/ios/Makefile b/ion/src/simulator/ios/Makefile index a8b0c36d6..2d85d1297 100644 --- a/ion/src/simulator/ios/Makefile +++ b/ion/src/simulator/ios/Makefile @@ -89,6 +89,4 @@ simulator_app_deps += $(call simulator_app_resource,launch.storyboardc) include ion/src/simulator/shared/apple/targets.mak -DEFAULT = epsilon.ipa - endif diff --git a/ion/src/simulator/macos/Makefile b/ion/src/simulator/macos/Makefile index 3992f08fa..81b06f3d4 100644 --- a/ion/src/simulator/macos/Makefile +++ b/ion/src/simulator/macos/Makefile @@ -43,6 +43,4 @@ simulator_app_deps += $(call simulator_app_resource,app.icns) include ion/src/simulator/shared/apple/targets.mak -DEFAULT = epsilon.app - endif From 1b2bbd163b7cc34e1c61c6f8961495d69e519f9c Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 2 Apr 2020 22:48:01 -0400 Subject: [PATCH 29/79] [ion/device] Rename a file --- ion/src/device/shared/drivers/Makefile | 2 +- ion/src/device/shared/drivers/{console.cpp => console_uart.cpp} | 0 ion/src/device/shared/usb/Makefile | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename ion/src/device/shared/drivers/{console.cpp => console_uart.cpp} (100%) diff --git a/ion/src/device/shared/drivers/Makefile b/ion/src/device/shared/drivers/Makefile index 386b519dd..548632a8e 100644 --- a/ion/src/device/shared/drivers/Makefile +++ b/ion/src/device/shared/drivers/Makefile @@ -3,7 +3,7 @@ ion_device_src += $(addprefix ion/src/device/shared/drivers/, \ battery.cpp \ base64.cpp \ board.cpp \ - console.cpp:+consoleuart \ + console_uart.cpp:+consoleuart \ console_dummy.cpp:-consoleuart \ crc32.cpp \ display.cpp \ diff --git a/ion/src/device/shared/drivers/console.cpp b/ion/src/device/shared/drivers/console_uart.cpp similarity index 100% rename from ion/src/device/shared/drivers/console.cpp rename to ion/src/device/shared/drivers/console_uart.cpp diff --git a/ion/src/device/shared/usb/Makefile b/ion/src/device/shared/usb/Makefile index 21c46dcbe..2e7ae45db 100644 --- a/ion/src/device/shared/usb/Makefile +++ b/ion/src/device/shared/usb/Makefile @@ -48,7 +48,7 @@ ion_device_dfu_src += $(addprefix ion/src/device/shared/drivers/, \ battery.cpp \ base64.cpp \ board.cpp \ - console.cpp \ + console_uart.cpp \ crc32.cpp \ display.cpp \ events_keyboard_platform.cpp \ From 18869189008e9e7326ae097e82c082fa3d5908eb Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 2 Apr 2020 22:51:12 -0400 Subject: [PATCH 30/79] [ci] Simplify bucket names --- .github/workflows/ci-workflow.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 8a49fc7f0..ed568d61b 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -2,7 +2,7 @@ name: Continuous integration on: [pull_request, push] jobs: - build-simulator-android: + android: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -13,7 +13,7 @@ jobs: with: name: epsilon-android.apk path: output/release/simulator/android/epsilon.apk - build-device-n0100: + n0100: runs-on: ubuntu-latest steps: - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config @@ -31,7 +31,7 @@ jobs: with: name: epsilon-n0100.dfu path: output/release/device/n0100/epsilon.dfu - build-device-n0110: + n0110: runs-on: ubuntu-latest steps: - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config @@ -51,7 +51,7 @@ jobs: with: name: epsilon-n0110.dfu path: output/release/device/n0110/epsilon.dfu - build-simulator-windows: + windows: runs-on: windows-latest steps: - uses: numworks/setup-msys2@v1 @@ -65,7 +65,7 @@ jobs: with: name: epsilon-windows.exe path: output/release/simulator/windows/epsilon.exe - build-simulator-web: + web: runs-on: ubuntu-latest steps: - uses: numworks/setup-emscripten@v1 @@ -80,7 +80,7 @@ jobs: with: name: epsilon-web.zip path: output/release/simulator/web/epsilon.zip - build-simulator-linux: + linux: runs-on: ubuntu-latest steps: - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config @@ -93,7 +93,7 @@ jobs: with: name: epsilon-linux.bin path: output/release/simulator/linux/epsilon.bin - build-simulator-macos: + macos: runs-on: macOS-latest steps: - run: brew install numworks/tap/epsilon-sdk @@ -106,7 +106,7 @@ jobs: with: name: epsilon-macos.zip path: output/release/simulator/macos/epsilon.app - build-simulator-ios: + ios: runs-on: macOS-latest steps: - run: brew install numworks/tap/epsilon-sdk From 13b5cd0151e6db8840b808c0ebb844e4d2b515a4 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 2 Apr 2020 22:52:39 -0400 Subject: [PATCH 31/79] [ci] Build for the iOS Simulator too This means building an x86_64 binary that will run on a Mac simulating an iPhone or an iPad --- .github/workflows/ci-workflow.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index ed568d61b..ca7d77355 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -114,6 +114,7 @@ jobs: - run: make -j2 PLATFORM=simulator TARGET=ios EPSILON_TELEMETRY=0 - run: make -j2 PLATFORM=simulator TARGET=ios EPSILON_TELEMETRY=0 epsilon.official.ipa - run: make -j2 PLATFORM=simulator TARGET=ios EPSILON_TELEMETRY=0 test.ipa + - run: make -j2 PLATFORM=simulator TARGET=ios EPSILON_TELEMETRY=0 APPLE_PLATFORM=ios-simulator - uses: actions/upload-artifact@master with: name: epsilon-ios.ipa From 6de90d97a455a83a5854533317694ed327c2141f Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 3 Apr 2020 08:26:50 -0400 Subject: [PATCH 32/79] [build] Remove duplication of HANDY_TARGETS_EXTENSIONS --- build/platform.simulator.web.mak | 2 ++ build/targets.simulator.android.mak | 4 ---- build/targets.simulator.ios.mak | 4 ---- build/targets.simulator.mak | 2 -- build/targets.simulator.web.mak | 2 -- 5 files changed, 2 insertions(+), 12 deletions(-) diff --git a/build/platform.simulator.web.mak b/build/platform.simulator.web.mak index 0a39c53f8..aa105edbd 100644 --- a/build/platform.simulator.web.mak +++ b/build/platform.simulator.web.mak @@ -1,2 +1,4 @@ TOOLCHAIN = emscripten EXE = js + +HANDY_TARGETS_EXTENSIONS += zip diff --git a/build/targets.simulator.android.mak b/build/targets.simulator.android.mak index 1cd826d3c..18980fc33 100644 --- a/build/targets.simulator.android.mak +++ b/build/targets.simulator.android.mak @@ -1,7 +1,3 @@ -ifndef ARCH -HANDY_TARGETS_EXTENSIONS += apk -endif - .PHONY: %_run %_run: $(BUILD_DIR)/%.apk $(call rule_label,ADB) diff --git a/build/targets.simulator.ios.mak b/build/targets.simulator.ios.mak index c5f26ad8d..4986049d5 100644 --- a/build/targets.simulator.ios.mak +++ b/build/targets.simulator.ios.mak @@ -1,7 +1,3 @@ -ifndef ARCH -HANDY_TARGETS_EXTENSIONS += apk -endif - ifeq ($(APPLE_PLATFORM),ios-simulator) .PHONY: %_run %_run: $(BUILD_DIR)/%.app diff --git a/build/targets.simulator.mak b/build/targets.simulator.mak index 942822600..6e6ed0fe6 100644 --- a/build/targets.simulator.mak +++ b/build/targets.simulator.mak @@ -1,10 +1,8 @@ # Headless targets $(eval $(call rule_for_epsilon_flavor,headless)) - HANDY_TARGETS += epsilon.headless $(BUILD_DIR)/test.headless.$(EXE): $(call flavored_object_for,$(test_runner_src),headless) - HANDY_TARGETS += test.headless -include build/targets.simulator.$(TARGET).mak diff --git a/build/targets.simulator.web.mak b/build/targets.simulator.web.mak index e8ef36832..ead0b515e 100644 --- a/build/targets.simulator.web.mak +++ b/build/targets.simulator.web.mak @@ -1,3 +1 @@ -HANDY_TARGETS_EXTENSIONS += zip - $(BUILD_DIR)/test.headless.js: EMSCRIPTEN_MODULARIZE = 0 From a840e7fb05d1feafb175a4ad4d6f5680f3164b76 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 3 Apr 2020 22:42:28 -0400 Subject: [PATCH 33/79] [liba] Define SEEK_CUR For some reason it was defined in Python and raised re-def errors --- liba/include/unistd.h | 1 + python/port/mpconfigport.h | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/liba/include/unistd.h b/liba/include/unistd.h index 8dadc4ad4..9b95be0b3 100644 --- a/liba/include/unistd.h +++ b/liba/include/unistd.h @@ -2,5 +2,6 @@ #define LIBA_UNISTD_H #define SEEK_SET 0 +#define SEEK_CUR 1 #endif diff --git a/python/port/mpconfigport.h b/python/port/mpconfigport.h index bfff743d6..53c3bb90a 100644 --- a/python/port/mpconfigport.h +++ b/python/port/mpconfigport.h @@ -119,8 +119,6 @@ typedef uintptr_t mp_uint_t; // must be pointer size typedef long mp_off_t; -#define SEEK_CUR 1 - // extra built in names to add to the global namespace #define MICROPY_PORT_BUILTINS \ { MP_OBJ_NEW_QSTR(MP_QSTR_open), (mp_obj_t)&mp_builtin_open_obj }, \ From 88912be9a8b86a77847182ae62cf5b6439c9dbaa Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 3 Apr 2020 22:30:58 -0400 Subject: [PATCH 34/79] [build] The all target can live in a standalone Makefile --- build/{targets.all.mak => all.mak} | 7 +++++-- build/targets.mak | 2 -- 2 files changed, 5 insertions(+), 4 deletions(-) rename build/{targets.all.mak => all.mak} (96%) diff --git a/build/targets.all.mak b/build/all.mak similarity index 96% rename from build/targets.all.mak rename to build/all.mak index cdb5c3633..ccd901bcc 100644 --- a/build/targets.all.mak +++ b/build/all.mak @@ -1,3 +1,6 @@ +# This is a standalone Makefile +# Invoke using "make -f build/all.mak" + ANDROID_GRADLE_KEYSTORE ?= ~/.gradle/google-play-upload.keystore ANDROID_GRADLE_PROPERTIES ?= ~/.gradle/gradle.properties IOS_MOBILE_PROVISION ?= build/artifacts/NumWorks_Graphing_Calculator_Distribution.mobileprovision @@ -23,8 +26,8 @@ define command_check fi endef -.PHONY: all_official -all_official: +.PHONY: all +all: $(call file_check,$(ANDROID_GRADLE_KEYSTORE)) $(call file_check,$(ANDROID_GRADLE_PROPERTIES)) $(call file_check,$(IOS_MOBILE_PROVISION)) diff --git a/build/targets.mak b/build/targets.mak index 85303eb5d..b570237f2 100644 --- a/build/targets.mak +++ b/build/targets.mak @@ -81,5 +81,3 @@ $(1).$(2): $$(BUILD_DIR)/$(1).$(2) endef $(foreach extension,$(HANDY_TARGETS_EXTENSIONS),$(foreach executable,$(HANDY_TARGETS),$(eval $(call handy_target_rule,$(executable),$(extension))))) - -include build/targets.all.mak From f450e36e65e8ca75bf1763c5995e006049c4d2d7 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 3 Apr 2020 22:33:11 -0400 Subject: [PATCH 35/79] [build/all] Use the short iOS and Android targets --- build/all.mak | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/all.mak b/build/all.mak index ccd901bcc..b2d2dfd66 100644 --- a/build/all.mak +++ b/build/all.mak @@ -55,9 +55,9 @@ all: $(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.python.js $(Q) echo "BUILD_FIRMWARE SIMULATOR ANDROID" $(Q) $(MAKE) PLATFORM=simulator TARGET=android clean - $(Q) $(MAKE) PLATFORM=simulator TARGET=android epsilon.official.signed.apk - $(Q) cp output/release/simulator/android/app/outputs/apk/codesigned/android-codesigned.apk output/all_official/epsilon.official.apk + $(Q) $(MAKE) PLATFORM=simulator TARGET=android epsilon.official.apk + $(Q) cp output/release/simulator/android/epsilon.official.apk output/all_official/epsilon.official.apk $(Q) echo "BUILD_FIRMWARE SIMULATOR IOS" $(Q) $(MAKE) PLATFORM=simulator TARGET=ios clean - $(Q) $(MAKE) PLATFORM=simulator TARGET=ios IOS_PROVISIONNING_PROFILE=$(IOS_MOBILE_PROVISION) output/release/simulator/ios/app/epsilon.official.ipa - $(Q) cp output/release/simulator/ios/app/epsilon.official.ipa output/all_official/epsilon.ipa + $(Q) $(MAKE) PLATFORM=simulator TARGET=ios IOS_PROVISIONNING_PROFILE=$(IOS_MOBILE_PROVISION) epsilon.official.ipa + $(Q) cp output/release/simulator/ios/epsilon.official.ipa output/all_official/epsilon.ipa From edde8e8eba249b991eeb7c9a5f388870a06983b0 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 3 Apr 2020 22:39:18 -0400 Subject: [PATCH 36/79] [build/all] Re-sourcing emsdk isn't needed --- build/all.mak | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/build/all.mak b/build/all.mak index b2d2dfd66..7abb3c585 100644 --- a/build/all.mak +++ b/build/all.mak @@ -6,10 +6,6 @@ ANDROID_GRADLE_PROPERTIES ?= ~/.gradle/gradle.properties IOS_MOBILE_PROVISION ?= build/artifacts/NumWorks_Graphing_Calculator_Distribution.mobileprovision EMCC ?= emcc -define source_emsdk -source ~/emsdk/emsdk_env.sh > /dev/null -endef - define file_check @ if test ! -f $(1); \ then \ @@ -44,14 +40,14 @@ all: $(Q) cp output/release/device/n0100/epsilon.official.onboarding.dfu output/all_official/epsilon.device.n0100.dfu $(Q) echo "BUILD_FIRMWARE SIMULATOR WEB ZIP" $(Q) $(MAKE) PLATFORM=simulator TARGET=web clean - $(Q) $(call source_emsdk); $(MAKE) PLATFORM=simulator TARGET=web epsilon.official.zip + $(Q) $(MAKE) PLATFORM=simulator TARGET=web epsilon.official.zip $(Q) cp output/release/simulator/web/epsilon.official.zip output/all_official/simulator.web.zip $(Q) echo "BUILD_FIRMWARE SIMULATOR WEB JS" - $(Q) $(call source_emsdk); $(MAKE) PLATFORM=simulator TARGET=web epsilon.official.js + $(Q) $(MAKE) PLATFORM=simulator TARGET=web epsilon.official.js $(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.js $(Q) echo "BUILD_FIRMWARE SIMULATOR WEB PYTHON JS" $(Q) $(MAKE) PLATFORM=simulator TARGET=web clean - $(Q) $(call source_emsdk); $(MAKE) PLATFORM=simulator TARGET=web EPSILON_GETOPT=1 EPSILON_APPS=code epsilon.official.js + $(Q) $(MAKE) PLATFORM=simulator TARGET=web EPSILON_GETOPT=1 EPSILON_APPS=code epsilon.official.js $(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.python.js $(Q) echo "BUILD_FIRMWARE SIMULATOR ANDROID" $(Q) $(MAKE) PLATFORM=simulator TARGET=android clean From 7d5cad32b14b02c0a2558a7077086cf1e87995dc Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Fri, 3 Apr 2020 22:39:47 -0400 Subject: [PATCH 37/79] [build/all] Q isn't defined (and isn't needed either) --- build/all.mak | 58 +++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/build/all.mak b/build/all.mak index 7abb3c585..9465a88e3 100644 --- a/build/all.mak +++ b/build/all.mak @@ -28,32 +28,32 @@ all: $(call file_check,$(ANDROID_GRADLE_PROPERTIES)) $(call file_check,$(IOS_MOBILE_PROVISION)) $(call command_check,$(EMCC)) - $(Q) rm -rf output/all_official - $(Q) mkdir -p output/all_official - $(Q) echo "BUILD_FIRMWARE DEVICE N0110" - $(Q) $(MAKE) clean - $(Q) $(MAKE) epsilon.official.onboarding.dfu - $(Q) cp output/release/device/n0110/epsilon.official.onboarding.dfu output/all_official/epsilon.device.n0110.dfu - $(Q) echo "BUILD_FIRMWARE DEVICE N0100" - $(Q) $(MAKE) MODEL=n0100 clean - $(Q) $(MAKE) MODEL=n0100 epsilon.official.onboarding.dfu - $(Q) cp output/release/device/n0100/epsilon.official.onboarding.dfu output/all_official/epsilon.device.n0100.dfu - $(Q) echo "BUILD_FIRMWARE SIMULATOR WEB ZIP" - $(Q) $(MAKE) PLATFORM=simulator TARGET=web clean - $(Q) $(MAKE) PLATFORM=simulator TARGET=web epsilon.official.zip - $(Q) cp output/release/simulator/web/epsilon.official.zip output/all_official/simulator.web.zip - $(Q) echo "BUILD_FIRMWARE SIMULATOR WEB JS" - $(Q) $(MAKE) PLATFORM=simulator TARGET=web epsilon.official.js - $(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.js - $(Q) echo "BUILD_FIRMWARE SIMULATOR WEB PYTHON JS" - $(Q) $(MAKE) PLATFORM=simulator TARGET=web clean - $(Q) $(MAKE) PLATFORM=simulator TARGET=web EPSILON_GETOPT=1 EPSILON_APPS=code epsilon.official.js - $(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.python.js - $(Q) echo "BUILD_FIRMWARE SIMULATOR ANDROID" - $(Q) $(MAKE) PLATFORM=simulator TARGET=android clean - $(Q) $(MAKE) PLATFORM=simulator TARGET=android epsilon.official.apk - $(Q) cp output/release/simulator/android/epsilon.official.apk output/all_official/epsilon.official.apk - $(Q) echo "BUILD_FIRMWARE SIMULATOR IOS" - $(Q) $(MAKE) PLATFORM=simulator TARGET=ios clean - $(Q) $(MAKE) PLATFORM=simulator TARGET=ios IOS_PROVISIONNING_PROFILE=$(IOS_MOBILE_PROVISION) epsilon.official.ipa - $(Q) cp output/release/simulator/ios/epsilon.official.ipa output/all_official/epsilon.ipa + @ rm -rf output/all_official + @ mkdir -p output/all_official + @ echo "BUILD_FIRMWARE DEVICE N0110" + @ $(MAKE) clean + @ $(MAKE) epsilon.official.onboarding.dfu + @ cp output/release/device/n0110/epsilon.official.onboarding.dfu output/all_official/epsilon.device.n0110.dfu + @ echo "BUILD_FIRMWARE DEVICE N0100" + @ $(MAKE) MODEL=n0100 clean + @ $(MAKE) MODEL=n0100 epsilon.official.onboarding.dfu + @ cp output/release/device/n0100/epsilon.official.onboarding.dfu output/all_official/epsilon.device.n0100.dfu + @ echo "BUILD_FIRMWARE SIMULATOR WEB ZIP" + @ $(MAKE) PLATFORM=simulator TARGET=web clean + @ $(MAKE) PLATFORM=simulator TARGET=web epsilon.official.zip + @ cp output/release/simulator/web/epsilon.official.zip output/all_official/simulator.web.zip + @ echo "BUILD_FIRMWARE SIMULATOR WEB JS" + @ $(MAKE) PLATFORM=simulator TARGET=web epsilon.official.js + @ cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.js + @ echo "BUILD_FIRMWARE SIMULATOR WEB PYTHON JS" + @ $(MAKE) PLATFORM=simulator TARGET=web clean + @ $(MAKE) PLATFORM=simulator TARGET=web EPSILON_GETOPT=1 EPSILON_APPS=code epsilon.official.js + @ cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.python.js + @ echo "BUILD_FIRMWARE SIMULATOR ANDROID" + @ $(MAKE) PLATFORM=simulator TARGET=android clean + @ $(MAKE) PLATFORM=simulator TARGET=android epsilon.official.apk + @ cp output/release/simulator/android/epsilon.official.apk output/all_official/epsilon.official.apk + @ echo "BUILD_FIRMWARE SIMULATOR IOS" + @ $(MAKE) PLATFORM=simulator TARGET=ios clean + @ $(MAKE) PLATFORM=simulator TARGET=ios IOS_PROVISIONNING_PROFILE=$(IOS_MOBILE_PROVISION) epsilon.official.ipa + @ cp output/release/simulator/ios/epsilon.official.ipa output/all_official/epsilon.ipa From 84f6b179de727744b359c687d09ee0231c40576f Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Sun, 5 Apr 2020 20:20:19 -0400 Subject: [PATCH 38/79] [build] rule_for now takes both "local" and "global" parameters Previous the build would fail on macOS if you had an epsilon.elf file in at the root of the project. Indeed, the %.elf -> %.bin rule would get triggered, but this rule should only operate on generated (intermediate) elf file. --- apps/Makefile | 3 ++- build/helpers.mk | 13 +++++++++---- build/rules.mk | 32 ++++++++++++++++++++------------ escher/Makefile | 6 ++++-- kandinsky/Makefile | 6 ++++-- python/Makefile | 3 ++- 6 files changed, 41 insertions(+), 22 deletions(-) diff --git a/apps/Makefile b/apps/Makefile index c1402113f..f31363b31 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -80,7 +80,8 @@ $(eval $(call rule_for, \ I18N, \ apps/i18n.cpp, \ $(i18n_files), \ - $$(PYTHON) apps/i18n.py --header $$(subst .cpp,.h,$$@) --implementation $$@ --locales $$(EPSILON_I18N) --files $$^ \ + $$(PYTHON) apps/i18n.py --header $$(subst .cpp,.h,$$@) --implementation $$@ --locales $$(EPSILON_I18N) --files $$^, \ + global \ )) $(BUILD_DIR)/apps/i18n.h: $(BUILD_DIR)/apps/i18n.cpp diff --git a/build/helpers.mk b/build/helpers.mk index b397ea96a..ab9934192 100644 --- a/build/helpers.mk +++ b/build/helpers.mk @@ -8,15 +8,20 @@ define rule_label @ echo "$(shell printf "%-8s" $(strip $(1)))$(@:$(BUILD_DIR)/%=%)" endef +# rule_for can define both global and local rules +# - use local if the source can be an intermediate file +# - use global if the source can be in the main tree define rule_for -ifeq ($(strip $(5)),with_local_version) +ifneq ($(filter global,$(5)),) +$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(strip $(3)) | $$$$(@D)/. + @ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)" + $(Q) $(4) +endif +ifneq ($(filter local,$(5)),) $(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(addprefix $$(BUILD_DIR)/,$(strip $(3))) @ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)" $(Q) $(4) endif -$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(strip $(3)) | $$$$(@D)/. - @ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)" - $(Q) $(4) endef # Helper functions to work with variants diff --git a/build/rules.mk b/build/rules.mk index 2826a3746..6698e3783 100644 --- a/build/rules.mk +++ b/build/rules.mk @@ -2,56 +2,62 @@ $(eval $(call rule_for, \ AS, %.o, %.s, \ - $$(CC) $$(SFLAGS) -c $$< -o $$@ \ + $$(CC) $$(SFLAGS) -c $$< -o $$@, \ + global \ )) $(eval $(call rule_for, \ CC, %.o, %.c, \ $$(CC) $$(CFLAGS) $$(SFLAGS) -c $$< -o $$@, \ - with_local_version \ + global local \ )) $(eval $(call rule_for, \ CPP, %, %.inc, \ - $$(CPP) -P $$< $$@ \ + $$(CPP) -P $$< $$@, \ + global \ )) $(eval $(call rule_for, \ CXX, %.o, %.cpp, \ $$(CXX) $$(CXXFLAGS) $$(SFLAGS) -c $$< -o $$@, \ - with_local_version \ + global local \ )) $(eval $(call rule_for, \ DFUSE, %.dfu, %.elf, \ $$(PYTHON) build/device/elf2dfu.py $$< $$@, \ - with_local_version \ + local \ )) $(eval $(call rule_for, \ OBJCOPY, %.hex, %.elf, \ - $$(OBJCOPY) -O ihex $$< $$@ \ + $$(OBJCOPY) -O ihex $$< $$@, \ + local \ )) $(eval $(call rule_for, \ OBJCOPY, %.bin, %.elf, \ $$(OBJCOPY) -O binary $$< $$@, \ - with_local_version \ + local \ )) $(eval $(call rule_for, \ OCC, %.o, %.m, \ - $$(CC) $$(CFLAGS) $$(SFLAGS) -c $$< -o $$@ \ + $$(CC) $$(CFLAGS) $$(SFLAGS) -c $$< -o $$@, \ + global \ )) $(eval $(call rule_for, \ OCC, %.o, %.mm, \ - $$(CXX) $$(CXXFLAGS) $$(SFLAGS) -c $$< -o $$@ \ + $$(CXX) $$(CXXFLAGS) $$(SFLAGS) -c $$< -o $$@, \ + global \ )) $(eval $(call rule_for, \ WINDRES, %.o, %.rc, \ - $$(WINDRES) $$< -O coff -o $$@ \ + $$(WINDRES) $$< -O coff -o $$@, \ + global \ )) ifdef EXE @@ -63,12 +69,14 @@ ifeq ($(OS),Windows_NT) # the linker to read its arguments from this file. $(eval $(call rule_for, \ LD, %.$$(EXE), , \ - echo $$^ > $$@.objs && $$(LD) @$$@.objs $$(LDFLAGS) -o $$@ && rm $$@.objs \ + echo $$^ > $$@.objs && $$(LD) @$$@.objs $$(LDFLAGS) -o $$@ && rm $$@.objs, \ + global \ )) else $(eval $(call rule_for, \ LD, %.$$(EXE), , \ - $$(LD) $$^ $$(LDFLAGS) -o $$@ \ + $$(LD) $$^ $$(LDFLAGS) -o $$@, \ + global \ )) endif endif diff --git a/escher/Makefile b/escher/Makefile index 6b88cd7be..d1caa59ed 100644 --- a/escher/Makefile +++ b/escher/Makefile @@ -94,7 +94,8 @@ $(eval $(call rule_for, \ HOSTCC, \ escher/image/inliner, \ escher/image/inliner.c $(addprefix ion/src/external/lz4/, lz4.c lz4hc.c), \ - $$(HOSTCC) -std=c99 `libpng-config --cflags` $$^ `libpng-config --ldflags` -o $$@ \ + $$(HOSTCC) -std=c99 `libpng-config --cflags` $$^ `libpng-config --ldflags` -o $$@, \ + global \ )) INLINER := $(BUILD_DIR)/escher/image/inliner @@ -104,7 +105,8 @@ $(eval $(call rule_for, \ INLINER, \ %.h %.cpp, \ %.png $$(INLINER), \ - $$(INLINER) $$< $$(basename $$@).h $$(basename $$@).cpp \ + $$(INLINER) $$< $$(basename $$@).h $$(basename $$@).cpp, \ + global \ )) # Mark a .cpp file as depending on a .png one diff --git a/kandinsky/Makefile b/kandinsky/Makefile index 150d4ea13..0deb00a90 100644 --- a/kandinsky/Makefile +++ b/kandinsky/Makefile @@ -38,7 +38,8 @@ $(eval $(call rule_for, \ HOSTCC, \ kandinsky/fonts/rasterizer, \ kandinsky/fonts/rasterizer.c $(addprefix ion/src/external/lz4/, lz4.c lz4hc.c), \ - $$(HOSTCC) $$(RASTERIZER_CFLAGS) $$^ $$(RASTERIZER_LDFLAGS) -o $$@ \ + $$(HOSTCC) $$(RASTERIZER_CFLAGS) $$^ $$(RASTERIZER_LDFLAGS) -o $$@, \ + global \ )) RASTERIZER := $(BUILD_DIR)/kandinsky/fonts/rasterizer @@ -49,7 +50,8 @@ $(call rule_for, \ RASTER, \ kandinsky/fonts/$(1).cpp, \ kandinsky/fonts/$(1).ttf $$(RASTERIZER), \ - $$(RASTERIZER) $$< $(2) $(2) $(3) $(4) $(1) $$@ $(if $(HAS_LIBPNG),$$(basename $$@).png) \ + $$(RASTERIZER) $$< $(2) $(2) $(3) $(4) $(1) $$@ $(if $(HAS_LIBPNG),$$(basename $$@).png), \ + global \ ) endef diff --git a/python/Makefile b/python/Makefile index 1f76017e3..6f1094a10 100644 --- a/python/Makefile +++ b/python/Makefile @@ -183,7 +183,8 @@ $(eval $(call rule_for, \ QSTRDAT, \ python/port/genhdr/qstrdefs.generated.h, \ python/port/genhdr/qstrdefs.in.h, \ - $$(PYTHON) python/src/py/makeqstrdata.py $$< > $$@ \ + $$(PYTHON) python/src/py/makeqstrdata.py $$< > $$@, \ + global \ )) $(call object_for,$(python_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h From bfb39618e1554fb68b27b8d0be6bb2bd30c52b0e Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Sun, 5 Apr 2020 15:27:30 -0400 Subject: [PATCH 39/79] [poincare] Add some missing std::move --- poincare/src/nth_root.cpp | 2 +- poincare/src/power.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/poincare/src/nth_root.cpp b/poincare/src/nth_root.cpp index db4748a47..b5e63ca01 100644 --- a/poincare/src/nth_root.cpp +++ b/poincare/src/nth_root.cpp @@ -50,7 +50,7 @@ Evaluation NthRootNode::templatedApproximate(Context * context, Preferences:: // root(x, q) with q integer and x real Complex result = PowerNode::computeNotPrincipalRealRootOfRationalPow(basec, (T)1.0, indexc.real()); if (!result.isUndefined()) { - return result; + return std::move(result); } } result = PowerNode::compute(basec, std::complex(1.0)/(indexc), complexFormat); diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index dab83ee30..fa7e3c755 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -325,7 +325,7 @@ template Evaluation PowerNode::templatedApproximate(Context * con } Complex result = computeNotPrincipalRealRootOfRationalPow(c, p, q); if (!result.isUndefined()) { - return result; + return std::move(result); } } defaultApproximation: From 53486409250b636c70febc25ab3f5b440db62513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20ANDR=C3=89?= Date: Sun, 5 Apr 2020 09:02:05 +0200 Subject: [PATCH 40/79] Traduction --- apps/home/base.fr.i18n | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/home/base.fr.i18n b/apps/home/base.fr.i18n index 158fa3bda..4a7f7130d 100644 --- a/apps/home/base.fr.i18n +++ b/apps/home/base.fr.i18n @@ -1,4 +1,4 @@ Apps = "Applications" AppsCapital = "APPLICATIONS" -ForbidenAppInExamMode1 = "This application is" -ForbidenAppInExamMode2 = "forbidden in exam mode" +ForbidenAppInExamMode1 = "Cette application n'est" +ForbidenAppInExamMode2 = "pas autorisée en mode examen." From 190802aa23c66d99e8a19be16b3b46fc7de14ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 1 Apr 2020 15:49:19 +0200 Subject: [PATCH 41/79] [python] Remove unused duplicate file --- python/port/mod/matplotlib/modpyplot_table.c | 33 -------------------- 1 file changed, 33 deletions(-) delete mode 100644 python/port/mod/matplotlib/modpyplot_table.c diff --git a/python/port/mod/matplotlib/modpyplot_table.c b/python/port/mod/matplotlib/modpyplot_table.c deleted file mode 100644 index c19aa128f..000000000 --- a/python/port/mod/matplotlib/modpyplot_table.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "modpyplot.h" - -STATIC MP_DEFINE_CONST_FUN_OBJ_0(modpyplot___init___obj, modpyplot___init__); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modpyplot_arrow_obj, 4, 4, modpyplot_arrow); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modpyplot_axis_obj, 0, 1, modpyplot_axis); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modpyplot_bar_obj, 2, 4, modpyplot_bar); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modpyplot_grid_obj, 0, 1, modpyplot_grid); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modpyplot_hist_obj, 1, 2, modpyplot_hist); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modpyplot_plot_obj, 1, 2, modpyplot_plot); -STATIC MP_DEFINE_CONST_FUN_OBJ_2(modpyplot_scatter_obj, modpyplot_scatter); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(modpyplot_show_obj, modpyplot_show); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(modpyplot_text_obj, modpyplot_text); - -STATIC const mp_rom_map_elem_t modpyplot_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_matplotlib_dot_pyplot) }, - { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&modpyplot___init___obj) }, - { MP_ROM_QSTR(MP_QSTR_arrow), MP_ROM_PTR(&modpyplot_arrow_obj) }, - { MP_ROM_QSTR(MP_QSTR_axis), MP_ROM_PTR(&modpyplot_axis_obj) }, - { MP_ROM_QSTR(MP_QSTR_bar), MP_ROM_PTR(&modpyplot_bar_obj) }, - { MP_ROM_QSTR(MP_QSTR_grid), MP_ROM_PTR(&modpyplot_grid_obj) }, - { MP_ROM_QSTR(MP_QSTR_hist), MP_ROM_PTR(&modpyplot_hist_obj) }, - { MP_ROM_QSTR(MP_QSTR_plot), MP_ROM_PTR(&modpyplot_plot_obj) }, - { MP_ROM_QSTR(MP_QSTR_scatter), MP_ROM_PTR(&modpyplot_scatter_obj) }, - { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&modpyplot_show_obj) }, - { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&modpyplot_text_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(modpyplot_module_globals, modpyplot_module_globals_table); - -const mp_obj_module_t modpyplot_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&modpyplot_module_globals, -}; From b7392dbd956954a9cda666a188f90d578e82b39a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 2 Apr 2020 10:04:45 +0200 Subject: [PATCH 42/79] [python] matplotlib: clean implementation (by using mp_obj_float_binary_op) --- .../port/mod/matplotlib/pyplot/modpyplot.cpp | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/python/port/mod/matplotlib/pyplot/modpyplot.cpp b/python/port/mod/matplotlib/pyplot/modpyplot.cpp index 0b1e5a808..b3dd445f2 100644 --- a/python/port/mod/matplotlib/pyplot/modpyplot.cpp +++ b/python/port/mod/matplotlib/pyplot/modpyplot.cpp @@ -88,8 +88,7 @@ mp_obj_t modpyplot_arrow(size_t n_args, const mp_obj_t *args) { assert(sPlotStore != nullptr); KDColor color = Palette::nextDataColor(&paletteIndex); - sPlotStore->addSegment(args[0], args[1], mp_obj_new_float(mp_obj_get_float(args[0])+mp_obj_get_float(args[2])), mp_obj_new_float(mp_obj_get_float(args[1])+mp_obj_get_float(args[3])), color, true); // TODO: use float_binary_op - + sPlotStore->addSegment(args[0], args[1], mp_obj_float_binary_op(MP_BINARY_OP_INPLACE_ADD, mp_obj_get_float(args[0]), args[2]), mp_obj_float_binary_op(MP_BINARY_OP_INPLACE_ADD, mp_obj_get_float(args[1]), args[3]), color, true); return mp_const_none; } @@ -183,13 +182,23 @@ mp_obj_t modpyplot_bar(size_t n_args, const mp_obj_t *args) { KDColor color = Palette::nextDataColor(&paletteIndex); for (size_t i=0; i 1 ? i : 0]); - mp_float_t iW = mp_obj_get_float(wItems[wLength > 1 ? i : 0]); - mp_float_t iB = mp_obj_get_float(bItems[bLength > 1 ? i : 0]); - mp_float_t iX = mp_obj_get_float(xItems[i])-iW/2.0; - mp_float_t iYStart = iH < 0.0 ? iB : iB + iH; - mp_float_t iYEnd = iH < 0.0 ? iB + iH : iB; - sPlotStore->addRect(mp_obj_new_float(iX), mp_obj_new_float(iX+iW), mp_obj_new_float(iYStart), mp_obj_new_float(iYEnd), color); // TODO: use float_binary_op? + mp_obj_t iH = hItems[hLength > 1 ? i : 0]; + mp_obj_t iW = wItems[wLength > 1 ? i : 0]; + mp_obj_t iB = bItems[bLength > 1 ? i : 0]; + mp_obj_t iX = xItems[i]; + + float iWf = mp_obj_get_float(iW); + float iXf = mp_obj_get_float(iX); + mp_obj_t rectLeft = mp_obj_new_float(iXf - iWf/2.0f); + mp_obj_t rectRight = mp_obj_new_float(iXf + iWf/2.0f); + mp_obj_t rectBottom = iB; + mp_obj_t rectTop = mp_obj_float_binary_op(MP_BINARY_OP_INPLACE_ADD, mp_obj_get_float(iH), iB); + if (mp_obj_get_float(iH) < 0.0) { + mp_obj_t temp = rectTop; + rectTop = rectBottom; + rectBottom = temp; + } + sPlotStore->addRect(rectLeft, rectRight, rectTop, rectBottom, color); } return mp_const_none; } From c58456a058b5aed41d905002778e13a7ad574a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 2 Apr 2020 14:51:52 +0200 Subject: [PATCH 43/79] [python] matplotlib: add comment WIP --- python/port/mod/matplotlib/pyplot/modpyplot.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/port/mod/matplotlib/pyplot/modpyplot.cpp b/python/port/mod/matplotlib/pyplot/modpyplot.cpp index b3dd445f2..652cf61c8 100644 --- a/python/port/mod/matplotlib/pyplot/modpyplot.cpp +++ b/python/port/mod/matplotlib/pyplot/modpyplot.cpp @@ -238,6 +238,8 @@ mp_obj_t modpyplot_hist(size_t n_args, const mp_obj_t *args) { mp_float_t min = mp_obj_get_float(xItems[0]); mp_float_t max = mp_obj_get_float(xItems[xLength - 1]); + // TODO: memory optimization + // Don't create a list of edges, compute the edge on the go if not present? mp_obj_t * edgeItems; size_t nBins; // bin arg From 19b5653851b3b166d0e5b1ccaa4db0f114a39c19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 6 Apr 2020 11:41:26 +0200 Subject: [PATCH 44/79] [apps/i18n] Remove Language::Default We never translate a i18n message to something else than the currrent language selected --- apps/empty_battery_window.cpp | 3 +-- apps/i18n.py | 13 ++++--------- apps/settings/main_controller.cpp | 2 +- apps/shared/language_controller.cpp | 5 ++--- escher/include/escher/i18n.h | 2 +- quiz/src/i18n.cpp | 2 +- 6 files changed, 10 insertions(+), 17 deletions(-) diff --git a/apps/empty_battery_window.cpp b/apps/empty_battery_window.cpp index b0e58e2dd..e324ae2a2 100644 --- a/apps/empty_battery_window.cpp +++ b/apps/empty_battery_window.cpp @@ -1,5 +1,4 @@ #include "empty_battery_window.h" -#include "global_preferences.h" #include extern "C" { #include @@ -12,7 +11,7 @@ EmptyBatteryWindow::EmptyBatteryWindow() : void EmptyBatteryWindow::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(bounds(), KDColorWhite); - const char * warningMessage = I18n::translate(I18n::Message::LowBattery, GlobalPreferences::sharedGlobalPreferences()->language()); + const char * warningMessage = I18n::translate(I18n::Message::LowBattery); KDSize warningSize = KDFont::LargeFont->stringSize(warningMessage); ctx->drawString(warningMessage, KDPoint((Ion::Display::Width - warningSize.width())/2, (Ion::Display::Height - warningSize.height())/2), KDFont::LargeFont); } diff --git a/apps/i18n.py b/apps/i18n.py index 4f4b44d49..43a3b9f2c 100644 --- a/apps/i18n.py +++ b/apps/i18n.py @@ -95,7 +95,6 @@ def print_header(data, path, locales): # Languages enumeration f.write("enum class Language : uint16_t {\n") - f.write(" Default = 0,\n") for locale in locales: f.write(" " + locale.upper() + ",\n") f.write("};\n\n") @@ -159,21 +158,17 @@ def print_implementation(data, path, locales): # Write the translate method - f.write("const char * translate(Message m, Language l) {\n") + f.write("const char * translate(Message m) {\n") f.write(" assert(m != Message::LocalizedMessageMarker);\n") f.write(" int localizedMessageOffset = (int)Message::LocalizedMessageMarker+1;\n") f.write(" if ((int)m < localizedMessageOffset) {\n") f.write(" assert(universalMessages[(int)m] != nullptr);\n") f.write(" return universalMessages[(int)m];\n") f.write(" }\n") - f.write(" int languageIndex = (int)l;\n") - f.write(" if (l == Language::Default) {\n") - f.write(" languageIndex = (int) GlobalPreferences::sharedGlobalPreferences()->language();\n") - f.write(" }\n") - f.write(" assert(languageIndex > 0);\n") + f.write(" int languageIndex = (int)GlobalPreferences::sharedGlobalPreferences()->language();\n") f.write(" int messageIndex = (int)m - localizedMessageOffset;\n") - f.write(" assert((messageIndex*NumberOfLanguages+languageIndex-1)*sizeof(char *) < sizeof(messages));\n") - f.write(" return messages[messageIndex][languageIndex-1];\n") + f.write(" assert((messageIndex*NumberOfLanguages+languageIndex)*sizeof(char *) < sizeof(messages));\n") + f.write(" return messages[messageIndex][languageIndex];\n") f.write("}\n\n") f.write("}\n") f.close() diff --git a/apps/settings/main_controller.cpp b/apps/settings/main_controller.cpp index b5fdd887c..3b5bf9b49 100644 --- a/apps/settings/main_controller.cpp +++ b/apps/settings/main_controller.cpp @@ -164,7 +164,7 @@ void MainController::willDisplayCellForIndex(HighlightCell * cell, int index) { MessageTableCell * myCell = (MessageTableCell *)cell; myCell->setMessage(title); if (index == k_indexOfLanguageCell) { - int index = (int)globalPreferences->language()-1; + int index = (int)(globalPreferences->language()); static_cast(cell)->setSubtitle(I18n::LanguageNames[index]); return; } diff --git a/apps/shared/language_controller.cpp b/apps/shared/language_controller.cpp index dafda8c0d..97f81123d 100644 --- a/apps/shared/language_controller.cpp +++ b/apps/shared/language_controller.cpp @@ -18,8 +18,7 @@ LanguageController::LanguageController(Responder * parentResponder, KDCoordinate void LanguageController::resetSelection() { m_selectableTableView.deselectTable(); - int index = (int)GlobalPreferences::sharedGlobalPreferences()->language()-1; - selectCellAtLocation(0, index); + selectCellAtLocation(0, (int)(GlobalPreferences::sharedGlobalPreferences()->language())); } const char * LanguageController::title() { @@ -41,7 +40,7 @@ void LanguageController::viewWillAppear() { bool LanguageController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::OK || event == Ion::Events::EXE) { - GlobalPreferences::sharedGlobalPreferences()->setLanguage((I18n::Language)(selectedRow()+1)); + GlobalPreferences::sharedGlobalPreferences()->setLanguage((I18n::Language)selectedRow()); /* We need to reload the whole title bar in order to translate both the * "Settings" title and the degree preference. */ AppsContainer::sharedAppsContainer()->reloadTitleBarView(); diff --git a/escher/include/escher/i18n.h b/escher/include/escher/i18n.h index cf3d7bb31..301b7ef9b 100644 --- a/escher/include/escher/i18n.h +++ b/escher/include/escher/i18n.h @@ -6,7 +6,7 @@ namespace I18n { enum class Message : uint16_t; enum class Language : uint16_t; - const char * translate(Message m, Language l = (Language)0); + const char * translate(Message m); int numberOfLanguages(); } diff --git a/quiz/src/i18n.cpp b/quiz/src/i18n.cpp index c9c871c3f..719c2a9c0 100644 --- a/quiz/src/i18n.cpp +++ b/quiz/src/i18n.cpp @@ -2,7 +2,7 @@ namespace I18n { -const char * translate(Message m, Language l) { +const char * translate(Message m) { return nullptr; } From efff509e2a7b96caeb8937dadb58e6cf5e50eece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 6 Apr 2020 11:52:55 +0200 Subject: [PATCH 45/79] [apps/global_preferences] DefaultLanguage is the first of the locales Not EN, because we might build without the EN locale --- apps/global_preferences.h | 3 ++- apps/i18n.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/global_preferences.h b/apps/global_preferences.h index ff0d5fb36..50f9aa0f1 100644 --- a/apps/global_preferences.h +++ b/apps/global_preferences.h @@ -25,8 +25,9 @@ public: void setFont(const KDFont * font) { m_font = font; } constexpr static int NumberOfBrightnessStates = 5; private: + static_assert(I18n::NumberOfLanguages > 0, "I18n::NumberOfLanguages is not superior to 0"); // There should already have be an error when processing an empty EPSILON_I18N flag GlobalPreferences() : - m_language(I18n::Language::EN), + m_language((I18n::Language)0), m_examMode(ExamMode::Unknown), m_showPopUp(true), m_brightnessLevel(Ion::Backlight::MaxBrightness), diff --git a/apps/i18n.py b/apps/i18n.py index 43a3b9f2c..b6e5dcdca 100644 --- a/apps/i18n.py +++ b/apps/i18n.py @@ -95,8 +95,10 @@ def print_header(data, path, locales): # Languages enumeration f.write("enum class Language : uint16_t {\n") + index = 0 for locale in locales: - f.write(" " + locale.upper() + ",\n") + f.write(" " + locale.upper() + (" = 0" if (index < 1) else "") +",\n") + index = index + 1 f.write("};\n\n") # Language names From 02738ba06ab152b5cffe9d3c12739b7313e306db Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Sun, 5 Apr 2020 15:07:53 -0400 Subject: [PATCH 46/79] [build] Add a tool to compare the size of two ELF files --- build/metrics/binary_size.py | 140 +++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 build/metrics/binary_size.py diff --git a/build/metrics/binary_size.py b/build/metrics/binary_size.py new file mode 100644 index 000000000..c9c07937c --- /dev/null +++ b/build/metrics/binary_size.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 + +import argparse +import os +import re +import subprocess +import urllib.parse + +# ELF analysis + +def loadable_sections(elf_file, address_prefix = ""): + objdump_section_headers_pattern = re.compile("^\s+\d+\s+(\.[\w\.]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+("+address_prefix+"[0-9a-f]+)\s+([0-9a-f]+).*LOAD", flags=re.MULTILINE) + objdump_output = subprocess.check_output(["arm-none-eabi-objdump", "-h", "-w", elf_file]).decode('utf-8') + sections = [] + for (name, size, vma, lma, offset) in re.findall(objdump_section_headers_pattern, objdump_output): + int_size = int(size,16) + if (int_size > 0): + sections.append({'name': name, 'size': int_size, 'vma': int(vma,16), 'lma': int(lma,16), 'offset':int(offset,16)}) + return sections + + +# Data filtering + +def biggest_sections(sections, n): + sorted_sections = sorted(sections, key=lambda s: s['size'], reverse=True) + return sorted_sections[:n] + +def total_size(sections): + return sum(map(lambda s: s['size'], sections)) + +def row_for_elf(elf, columns): + sections = loadable_sections(elf) + result = {} + for s in biggest_sections(sections, columns): + result[s['name']] = s['size'] + result['Total'] = total_size(sections) + return result + + +# String formatting + +def iso_separate(string): + space = ' ' # We may want to use a thin non-breaking space as thousands separator + return string.replace('_',space).replace('+','+'+space).replace('-','-'+space) + +def format_bytes(value, force_sign=False): + if value is None: + return '' + number_format = '{:' + if force_sign: + number_format += '+' + number_format += '_} bytes' + return iso_separate(number_format.format(value)) + +def format_percentage(value): + if value is None: + return '' + return iso_separate("{:+.1f} %".format(100*value)) + + +# Markdown + +def emphasize(string): + if string: + return '_' + string + '_' + else: + return '' + +def strong(string): + if string: + return '**' + string + '**' + else: + return '' + + +# Deltas + +def absolute_delta(x,y): + if x is None or y is None: + return None + return x-y + +def ratio_delta(x,y): + if x is None or y is None: + return None + return (x-y)/y + + +# Table formatting + +def format_row(row, header=False): + result = '|' + if header: + result += strong(header) + result += '|' + for v in row: + result += v + result += '|' + result += '\n' + return result + +def format_table(table): + base = table[0]['values'] + listed_sections = base.keys() + result = '' + result += format_row(listed_sections) + result += '|-|' + '-:|'*len(listed_sections) + '\n' + for i,row in enumerate(table): + v = row['values'] + result += format_row((format_bytes(v.get(s)) for s in listed_sections), header=row['label']) + if i != 0: + result += format_row(emphasize(format_bytes(absolute_delta(v.get(s), base.get(s)), force_sign=True)) for s in listed_sections) + result += format_row(emphasize(format_percentage(ratio_delta(v.get(s), base.get(s)))) for s in listed_sections) + return result + + +# Argument parsing + +parser = argparse.ArgumentParser(description='Compute binary size metrics') +parser.add_argument('files', type=str, nargs='+', help='an ELF file') +parser.add_argument('--labels', type=str, nargs='+', help='label for ELF file') +parser.add_argument('--number-of-sections', type=int, default=2, help='Number of detailed sections') +parser.add_argument('--escape', action='store_true', help='Escape the output') +args = parser.parse_args() + + +# Execution + +table = [] +for i,filename in enumerate(args.files): + label = os.path.basename(filename) + if args.labels and i < len(args.labels): + label = args.labels[i] + table.append({'label': label, 'values': row_for_elf(filename, args.number_of_sections)}) +formatted_table = format_table(table) + +if args.escape: + print(urllib.parse.quote(formatted_table, safe='| :*+')) +else: + print(formatted_table) From e34811ff18f0c39c7d72d32e70f53317c5291592 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Sun, 5 Apr 2020 15:09:45 -0400 Subject: [PATCH 47/79] [ci] Report metrics on each PR --- .github/workflows/metrics-workflow.yml | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/metrics-workflow.yml diff --git a/.github/workflows/metrics-workflow.yml b/.github/workflows/metrics-workflow.yml new file mode 100644 index 000000000..eb99181a1 --- /dev/null +++ b/.github/workflows/metrics-workflow.yml @@ -0,0 +1,33 @@ +name: Metrics +on: [pull_request] + +jobs: + binary-size: + runs-on: ubuntu-latest + steps: + - name: Install dependencies + run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config + - name: Install ARM toolchain + uses: numworks/setup-arm-toolchain@v1 + - name: Checkout PR base + uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.base.sha }} + path: base + - name: Build base + run: make -j2 -C base epsilon.elf + - name: Checkout PR head + uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + path: head + - name: Build head + run: make -j2 -C head epsilon.elf + - name: Retrieve binary size analysis + run: echo "::set-env name=BINARY_SIZE::$(python3 head/build/metrics/binary_size.py base/output/release/device/n0110/epsilon.elf head/output/release/device/n0110/epsilon.elf --labels Base Head --escape)" + - name: Add comment + uses: actions/github@v1.0.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + args: comment ${{ env.BINARY_SIZE }} From 1c9b4026450fb2be69b1e220f0b9c3762d2ad7e7 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 27 Feb 2020 23:09:31 +0000 Subject: [PATCH 48/79] [ion] Remove volatile qualifier from millis return type Qualification of an integer return type as volatile is ignored by the compiler. To clarify, an integer value (not a variable), which is what the return type is, can not be assigned to anyway, so qualification as volatile does not make sense. --- ion/include/ion/timing.h | 2 +- ion/src/device/shared/drivers/timing.cpp | 2 +- ion/src/shared/timing.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ion/include/ion/timing.h b/ion/include/ion/timing.h index 3a3b0c9ca..523232f8e 100644 --- a/ion/include/ion/timing.h +++ b/ion/include/ion/timing.h @@ -11,7 +11,7 @@ void msleep(uint32_t ms); /* millis is the number of milliseconds ellapsed since a random epoch. * On the device, epoch is the boot time. */ -volatile uint64_t millis(); +uint64_t millis(); } } diff --git a/ion/src/device/shared/drivers/timing.cpp b/ion/src/device/shared/drivers/timing.cpp index 8ec2740ad..62306d306 100644 --- a/ion/src/device/shared/drivers/timing.cpp +++ b/ion/src/device/shared/drivers/timing.cpp @@ -29,7 +29,7 @@ void usleep(uint32_t us) { } } -volatile uint64_t millis() { +uint64_t millis() { return MillisElapsed; } diff --git a/ion/src/shared/timing.cpp b/ion/src/shared/timing.cpp index 7b153a136..a9bf44567 100644 --- a/ion/src/shared/timing.cpp +++ b/ion/src/shared/timing.cpp @@ -3,7 +3,7 @@ static auto start = std::chrono::steady_clock::now(); -volatile uint64_t Ion::Timing::millis() { +uint64_t Ion::Timing::millis() { auto elapsed = std::chrono::steady_clock::now() - start; return std::chrono::duration_cast(elapsed).count(); } From bc7d59851792b140abd8d65f0baf644cb1e6e54c Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 27 Feb 2020 23:39:52 +0000 Subject: [PATCH 49/79] [ion] do not increment volatile variable in isr_systick In 2019 a proposal was approved which is deprecating this and other harmful usage of volatile in C++ in 2020. See web links at the end. Note that this did not at all change the GCC-generated machine code. Deprecating volatile (adopted in 2019 for C++20): http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1152r0.html http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1152r4.html Related, but less relevant: volatile_load and volatile_store: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1382r1.pdf Deprecating volatile: library: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1831r0.html --- ion/src/device/shared/boot/rt0.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ion/src/device/shared/boot/rt0.cpp b/ion/src/device/shared/boot/rt0.cpp index 8e8f2e39e..45a003f40 100644 --- a/ion/src/device/shared/boot/rt0.cpp +++ b/ion/src/device/shared/boot/rt0.cpp @@ -119,5 +119,7 @@ void __attribute__((noinline)) start() { } void __attribute__((interrupt, noinline)) isr_systick() { - Ion::Device::Timing::MillisElapsed++; + auto t = Ion::Device::Timing::MillisElapsed; + t++; + Ion::Device::Timing::MillisElapsed = t; } From 77af9ab92b1a02a4a833093fd16918d91ffe7ee9 Mon Sep 17 00:00:00 2001 From: Joachim LF Date: Mon, 6 Apr 2020 18:06:44 +0200 Subject: [PATCH 50/79] I18NWarn --- apps/Makefile | 2 +- apps/i18n.py | 44 +++++++++++++++++++++++++++++++++++++------- kandinsky/Makefile | 2 ++ 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/apps/Makefile b/apps/Makefile index f31363b31..ee63f399b 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -80,7 +80,7 @@ $(eval $(call rule_for, \ I18N, \ apps/i18n.cpp, \ $(i18n_files), \ - $$(PYTHON) apps/i18n.py --header $$(subst .cpp,.h,$$@) --implementation $$@ --locales $$(EPSILON_I18N) --files $$^, \ + $$(PYTHON) apps/i18n.py --codepoints $(code_points) --header $$(subst .cpp,.h,$$@) --implementation $$@ --locales $$(EPSILON_I18N) --files $$^, \ global \ )) diff --git a/apps/i18n.py b/apps/i18n.py index b6e5dcdca..6ed920a40 100644 --- a/apps/i18n.py +++ b/apps/i18n.py @@ -12,6 +12,19 @@ import unicodedata import argparse import io + +parser = argparse.ArgumentParser(description="Process some i18n files.") +parser.add_argument('--header', help='the .h file to generate') +parser.add_argument('--implementation', help='the .cpp file to generate') +parser.add_argument('--locales', nargs='+', help='locale to actually generate') +parser.add_argument('--codepoints', help='the code_points.h file') +parser.add_argument('--files', nargs='+', help='an i18n file') + +args = parser.parse_args() + +def has_glyph(glyph): + return glyph in codepoints + def source_definition(i18n_string): s = unicodedata.normalize("NFKD", i18n_string) result = u"\"" @@ -29,6 +42,9 @@ def source_definition(i18n_string): # Remove the uppercase characters with combining chars checkForCombining = s[i].isupper() result = result + s[i] + if not has_glyph(s[i]): + sys.stderr.write(s[i] + " (" + str(hex(ord(s[i]))) + ") is not a character present in " + args.codepoints + " . Exiting !\n") + sys.exit(-1) i = i+1 result = result + u"\"" return result.encode("utf-8") @@ -73,6 +89,27 @@ def parse_files(files): data[locale][name] = definition return {"messages": sorted(messages), "universal_messages": sorted(universal_messages), "data": data} +def parse_codepoints(file): + codepoints = [] + with io.open(file, "r", encoding='utf-8') as file: + IsCodePoint = False + for line in file: + if "};" in line: + IsCodePoint = False + if IsCodePoint: + start = line.find('0x') + stop = line.find(',') + if not (start == -1 or stop == -1): + hexstring = line[start:stop] + value = int(hexstring, 16) + char = chr(value) + codepoints.append(char) + if "CodePoints[]" in line: + IsCodePoint = True + return codepoints + +codepoints = parse_codepoints(args.codepoints) + def print_header(data, path, locales): f = open(path, "w") f.write("#ifndef APPS_I18N_H\n") @@ -175,13 +212,6 @@ def print_implementation(data, path, locales): f.write("}\n") f.close() -parser = argparse.ArgumentParser(description="Process some i18n files.") -parser.add_argument('--header', help='the .h file to generate') -parser.add_argument('--implementation', help='the .cpp file to generate') -parser.add_argument('--locales', nargs='+', help='locale to actually generate') -parser.add_argument('--files', nargs='+', help='an i18n file') - -args = parser.parse_args() data = parse_files(args.files) if args.header: print_header(data, args.header, args.locales) diff --git a/kandinsky/Makefile b/kandinsky/Makefile index 0deb00a90..1e1ad24ff 100644 --- a/kandinsky/Makefile +++ b/kandinsky/Makefile @@ -25,6 +25,8 @@ tests_src += $(addprefix kandinsky/test/,\ rect.cpp\ ) +code_points = kandinsky/fonts/code_points.h + RASTERIZER_CFLAGS := -std=c99 $(shell pkg-config freetype2 --cflags) RASTERIZER_LDFLAGS := $(shell pkg-config freetype2 --libs) From 7e02a90b2a33d45778bbb5870f7126c515375a1d Mon Sep 17 00:00:00 2001 From: Joachim LF Date: Mon, 6 Apr 2020 18:15:43 +0200 Subject: [PATCH 51/79] Don't scan not loaded script in toolbox --- apps/code/script_store.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/code/script_store.cpp b/apps/code/script_store.cpp index e21047d7c..25b9dce67 100644 --- a/apps/code/script_store.cpp +++ b/apps/code/script_store.cpp @@ -36,6 +36,12 @@ bool ScriptStore::isFull() { void ScriptStore::scanScriptsForFunctionsAndVariables(void * context, ScanCallback storeFunction, ScanCallback storeVariable) { for (int scriptIndex = 0; scriptIndex < numberOfScripts(); scriptIndex++) { + + //Don't scan not loaded script + if (!scriptAtIndex(scriptIndex).importationStatus()){ + continue; + } + // Handle lexer or parser errors with nlr. nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { From e314f2eb65fc52226045019edb532b9d85876e03 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Mon, 6 Apr 2020 18:07:27 -0400 Subject: [PATCH 52/79] [ci] Use steps to pass the binary_size result --- .github/workflows/metrics-workflow.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/metrics-workflow.yml b/.github/workflows/metrics-workflow.yml index eb99181a1..bc22fcd4b 100644 --- a/.github/workflows/metrics-workflow.yml +++ b/.github/workflows/metrics-workflow.yml @@ -24,10 +24,13 @@ jobs: - name: Build head run: make -j2 -C head epsilon.elf - name: Retrieve binary size analysis - run: echo "::set-env name=BINARY_SIZE::$(python3 head/build/metrics/binary_size.py base/output/release/device/n0110/epsilon.elf head/output/release/device/n0110/epsilon.elf --labels Base Head --escape)" + id: binary_size + run: echo "::set-output name=table::$(python3 head/build/metrics/binary_size.py base/output/release/device/n0110/epsilon.elf head/output/release/device/n0110/epsilon.elf --labels Base Head --escape)" + - name: Prepare comment auth + run: echo "::set-env name=GITHUB_TOKEN::$(echo ZGExNWM1YzNlMjVkMWU5ZGFmOWQyY2UxMmRhYjJiN2ZhMWM4ODVhMA== | base64 --decode)" - name: Add comment uses: actions/github@v1.0.0 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }} with: - args: comment ${{ env.BINARY_SIZE }} + args: comment ${{ steps.binary_size.outputs.table }} From 154625a9452b7169a623c981196074c6dce184c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 6 Apr 2020 13:27:36 +0200 Subject: [PATCH 53/79] [python] Fix warning: comparisons between signed and unsigned integers --- python/port/mod/matplotlib/pyplot/modpyplot.cpp | 4 ++-- python/port/mod/matplotlib/pyplot/plot_store.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/port/mod/matplotlib/pyplot/modpyplot.cpp b/python/port/mod/matplotlib/pyplot/modpyplot.cpp index 652cf61c8..e8bfff604 100644 --- a/python/port/mod/matplotlib/pyplot/modpyplot.cpp +++ b/python/port/mod/matplotlib/pyplot/modpyplot.cpp @@ -263,7 +263,7 @@ mp_obj_t modpyplot_hist(size_t n_args, const mp_obj_t *args) { } // Fill the bin edges list - for (int i = 0; i < nBins+1; i++) { + for (size_t i = 0; i < nBins+1; i++) { edgeItems[i] = mp_obj_new_float(min+i*binWidth); } } @@ -334,7 +334,7 @@ mp_obj_t modpyplot_plot(size_t n_args, const mp_obj_t *args) { // Create the default xItems: [0, 1, 2,...] xItems = m_new(mp_obj_t, length); - for (int i = 0; i < length; i++) { + for (size_t i = 0; i < length; i++) { xItems[i] = mp_obj_new_float((float)i); } } else { diff --git a/python/port/mod/matplotlib/pyplot/plot_store.cpp b/python/port/mod/matplotlib/pyplot/plot_store.cpp index ede1a3af4..9647044b6 100644 --- a/python/port/mod/matplotlib/pyplot/plot_store.cpp +++ b/python/port/mod/matplotlib/pyplot/plot_store.cpp @@ -62,7 +62,7 @@ T PlotStore::ListIterator::operator*() { }; void checkFloatType(mp_obj_t * elements, size_t nbOfElements) { - for (int i = 0; i < nbOfElements; i++) { + for (size_t i = 0; i < nbOfElements; i++) { // TODO: we don't take advantage of the fact that we extracted the value at the sametime... Maybe change the way things are done, build the c objects in addItem instead of allocating them on the python heap? Or use float array in python? mp_float_t value; if (!mp_obj_get_float_maybe(elements[i], &value)) { From e1d7b6bb0466b90c7e490ae02653ff8e96f95a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 6 Apr 2020 13:33:38 +0200 Subject: [PATCH 54/79] [apps] Avoid unsigned/signed comparison: storeChecksumAtIndex takes an int as parameter --- apps/regression/graph_controller.cpp | 2 +- apps/regression/graph_controller.h | 2 +- apps/shared/function_graph_controller.cpp | 2 +- apps/shared/function_graph_controller.h | 2 +- apps/shared/function_store.cpp | 2 +- apps/shared/function_store.h | 2 +- apps/shared/interactive_curve_view_controller.h | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index 93b5c5867..ebdebbb46 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -351,7 +351,7 @@ uint32_t GraphController::modelVersion() { return m_store->storeChecksum(); } -uint32_t GraphController::modelVersionAtIndex(size_t i) { +uint32_t GraphController::modelVersionAtIndex(int i) { assert(i < numberOfMemoizedVersions()); return *(m_store->seriesChecksum() + i); } diff --git a/apps/regression/graph_controller.h b/apps/regression/graph_controller.h index 30e722552..d81680c18 100644 --- a/apps/regression/graph_controller.h +++ b/apps/regression/graph_controller.h @@ -43,7 +43,7 @@ private: // InteractiveCurveViewController void initCursorParameters() override; uint32_t modelVersion() override; - uint32_t modelVersionAtIndex(size_t i) override; + uint32_t modelVersionAtIndex(int i) override; uint32_t rangeVersion() override; size_t numberOfMemoizedVersions() const override { return Store::k_numberOfSeries; } int selectedCurveIndex() const override { return *m_selectedSeriesIndex; } diff --git a/apps/shared/function_graph_controller.cpp b/apps/shared/function_graph_controller.cpp index f851f65ef..e1922ef6b 100644 --- a/apps/shared/function_graph_controller.cpp +++ b/apps/shared/function_graph_controller.cpp @@ -183,7 +183,7 @@ uint32_t FunctionGraphController::modelVersion() { return functionStore()->storeChecksum(); } -uint32_t FunctionGraphController::modelVersionAtIndex(size_t i) { +uint32_t FunctionGraphController::modelVersionAtIndex(int i) { return functionStore()->storeChecksumAtIndex(i); } diff --git a/apps/shared/function_graph_controller.h b/apps/shared/function_graph_controller.h index fcb15ae57..9e7297efe 100644 --- a/apps/shared/function_graph_controller.h +++ b/apps/shared/function_graph_controller.h @@ -50,7 +50,7 @@ private: // InteractiveCurveViewController bool moveCursorVertically(int direction) override; uint32_t modelVersion() override; - uint32_t modelVersionAtIndex(size_t i) override; + uint32_t modelVersionAtIndex(int i) override; uint32_t rangeVersion() override; size_t numberOfMemoizedVersions() const override { return sNumberOfMemoizedModelVersions; } diff --git a/apps/shared/function_store.cpp b/apps/shared/function_store.cpp index 7a05715a2..4ef45d956 100644 --- a/apps/shared/function_store.cpp +++ b/apps/shared/function_store.cpp @@ -6,7 +6,7 @@ uint32_t FunctionStore::storeChecksum() { return Ion::Storage::sharedStorage()->checksum(); } -uint32_t FunctionStore::storeChecksumAtIndex(size_t i) { +uint32_t FunctionStore::storeChecksumAtIndex(int i) { if (numberOfActiveFunctions() <= i) { return 0; } diff --git a/apps/shared/function_store.h b/apps/shared/function_store.h index 03e57ef58..0233a37dd 100644 --- a/apps/shared/function_store.h +++ b/apps/shared/function_store.h @@ -13,7 +13,7 @@ class FunctionStore : public ExpressionModelStore { public: FunctionStore() : ExpressionModelStore() {} uint32_t storeChecksum(); - uint32_t storeChecksumAtIndex(size_t i); + uint32_t storeChecksumAtIndex(int i); int numberOfActiveFunctions() const { return numberOfModelsSatisfyingTest(&isFunctionActive, nullptr); } diff --git a/apps/shared/interactive_curve_view_controller.h b/apps/shared/interactive_curve_view_controller.h index 9ef048f18..c732ff524 100644 --- a/apps/shared/interactive_curve_view_controller.h +++ b/apps/shared/interactive_curve_view_controller.h @@ -41,7 +41,7 @@ protected: virtual void initCursorParameters() = 0; virtual bool moveCursorVertically(int direction) = 0; virtual uint32_t modelVersion() = 0; - virtual uint32_t modelVersionAtIndex(size_t i) = 0; + virtual uint32_t modelVersionAtIndex(int i) = 0; virtual uint32_t rangeVersion() = 0; bool isCursorVisible(); From 26aab94ac0da81952fe27215013de510af33c59e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 6 Apr 2020 13:34:31 +0200 Subject: [PATCH 55/79] [apps/shared] Avoid signed/unsigned int comparison warning --- apps/shared/expression_model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/shared/expression_model.cpp b/apps/shared/expression_model.cpp index c67d399b7..cf698f7c2 100644 --- a/apps/shared/expression_model.cpp +++ b/apps/shared/expression_model.cpp @@ -34,7 +34,7 @@ void ExpressionModel::text(const Storage::Record * record, char * buffer, size_t e = e.replaceSymbolWithExpression(Symbol::Builder(UCodePointUnknown), Symbol::Builder(symbol)); } int serializedSize = e.serialize(buffer, bufferSize); - if (serializedSize >= bufferSize - 1) { + if (serializedSize >= (int)bufferSize - 1) { // It is very likely that the buffer is overflowed buffer[0] = 0; } From 010fb1894fc230edd6b67e51a0c1828da3b8736b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 6 Apr 2020 14:44:47 +0200 Subject: [PATCH 56/79] [python] Remove mpversion useless header --- python/port/genhdr/mpversion.h | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 python/port/genhdr/mpversion.h diff --git a/python/port/genhdr/mpversion.h b/python/port/genhdr/mpversion.h deleted file mode 100644 index 41fb02eec..000000000 --- a/python/port/genhdr/mpversion.h +++ /dev/null @@ -1,4 +0,0 @@ -// This file was generated by py/makeversionhdr.py -#define MICROPY_GIT_TAG "v1.11-dirty" -#define MICROPY_GIT_HASH "6f75c4f3c-dirty" -#define MICROPY_BUILD_DATE "2019-08-21" From 7df8c2935ab9486d45a4dbb8a1b7652fec1a4de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 6 Apr 2020 14:57:17 +0200 Subject: [PATCH 57/79] [python] upgrade to micropython 1.12 --- python/Makefile | 4 + python/port/genhdr/moduledefs.h | 8 +- python/port/port.cpp | 4 +- python/src/extmod/modurandom.c | 4 + python/src/py/asmarm.c | 6 +- python/src/py/asmbase.c | 4 +- python/src/py/asmbase.h | 2 +- python/src/py/asmxtensa.c | 42 +- python/src/py/asmxtensa.h | 80 ++- python/src/py/bc.c | 144 ++--- python/src/py/bc.h | 201 ++++++- python/src/py/bc0.h | 186 +++--- python/src/py/binary.c | 28 +- python/src/py/binary.h | 16 +- python/src/py/builtin.h | 3 +- python/src/py/builtinhelp.c | 4 - python/src/py/builtinimport.c | 39 +- python/src/py/compile.c | 104 ++-- python/src/py/compile.h | 4 +- python/src/py/emit.h | 13 +- python/src/py/emitbc.c | 335 +++++------ python/src/py/emitglue.c | 17 +- python/src/py/emitglue.h | 13 +- python/src/py/emitnative.c | 195 +++++-- python/src/py/emitnx86.c | 12 +- python/src/py/emitnxtensawin.c | 23 + python/src/py/grammar.h | 10 +- python/src/py/lexer.c | 6 +- python/src/py/lexer.h | 63 +- python/src/py/makeqstrdata.py | 6 +- python/src/py/modarray.c | 6 +- python/src/py/modio.c | 11 +- python/src/py/modmath.c | 39 ++ python/src/py/modmicropython.c | 2 +- python/src/py/modstruct.c | 8 +- python/src/py/modsys.c | 59 +- python/src/py/moduerrno.c | 4 +- python/src/py/mpconfig.h | 59 +- python/src/py/mphal.h | 4 + python/src/py/mpstate.h | 21 +- python/src/py/nativeglue.c | 141 ++++- python/src/py/nativeglue.h | 177 ++++++ python/src/py/nlr.h | 29 +- python/src/py/nlrpowerpc.c | 121 ++++ python/src/py/nlrthumb.c | 22 +- python/src/py/nlrx64.c | 2 +- python/src/py/nlrx86.c | 2 +- python/src/py/nlrxtensa.c | 2 +- python/src/py/obj.c | 4 +- python/src/py/obj.h | 1 - python/src/py/objarray.c | 3 +- python/src/py/objdict.c | 12 +- python/src/py/objenumerate.c | 2 +- python/src/py/objexcept.c | 7 +- python/src/py/objfun.c | 34 +- python/src/py/objfun.h | 3 + python/src/py/objgenerator.c | 81 ++- python/src/py/objint.c | 2 +- python/src/py/objmodule.c | 29 +- python/src/py/objmodule.h | 4 +- python/src/py/objstr.c | 9 +- python/src/py/objstringio.c | 14 +- python/src/py/objtuple.c | 8 +- python/src/py/objtype.c | 20 +- python/src/py/parse.c | 46 +- python/src/py/parsenum.c | 2 +- python/src/py/persistentcode.c | 295 ++++++---- python/src/py/persistentcode.h | 61 +- python/src/py/profile.c | 984 ++++++++++++++++++++++++++++++++ python/src/py/profile.h | 79 +++ python/src/py/qstr.h | 4 +- python/src/py/repl.c | 2 +- python/src/py/ringbuf.c | 73 +++ python/src/py/ringbuf.h | 18 +- python/src/py/runtime.c | 52 +- python/src/py/runtime.h | 4 - python/src/py/runtime0.h | 131 ++--- python/src/py/scheduler.c | 8 +- python/src/py/sequence.c | 2 +- python/src/py/showbc.c | 53 +- python/src/py/stream.h | 9 +- python/src/py/vm.c | 445 +++++++++------ python/src/py/vmentrytable.h | 14 +- 83 files changed, 3516 insertions(+), 1284 deletions(-) create mode 100644 python/src/py/emitnxtensawin.c create mode 100644 python/src/py/nativeglue.h create mode 100644 python/src/py/nlrpowerpc.c create mode 100644 python/src/py/profile.c create mode 100644 python/src/py/profile.h create mode 100644 python/src/py/ringbuf.c diff --git a/python/Makefile b/python/Makefile index 6f1094a10..503d7fad1 100644 --- a/python/Makefile +++ b/python/Makefile @@ -13,6 +13,7 @@ py_src = $(addprefix python/src/py/,\ nlrx86.c \ nlrx64.c \ nlrthumb.c \ + nlrpowerpc.c \ nlrxtensa.c \ nlrsetjmp.c \ malloc.c \ @@ -43,6 +44,7 @@ py_src = $(addprefix python/src/py/,\ asmxtensa.c \ emitnxtensa.c \ emitinlinextensa.c \ + emitnxtensawin.c \ formatfloat.c \ parsenumbase.c \ parsenum.c \ @@ -52,9 +54,11 @@ py_src = $(addprefix python/src/py/,\ runtime_utils.c \ scheduler.c \ nativeglue.c \ + ringbuf.c \ stackctrl.c \ argcheck.c \ warning.c \ + profile.c \ map.c \ obj.c \ objarray.c \ diff --git a/python/port/genhdr/moduledefs.h b/python/port/genhdr/moduledefs.h index 9f758732c..c86562df8 100644 --- a/python/port/genhdr/moduledefs.h +++ b/python/port/genhdr/moduledefs.h @@ -1,13 +1,13 @@ // Automatically generated by makemoduledefs.py. #if (MICROPY_PY_ARRAY) - extern const struct _mp_obj_module_t mp_module_array; - #define MODULE_DEF_MP_QSTR_ARRAY { MP_ROM_QSTR(MP_QSTR_array), MP_ROM_PTR(&mp_module_array) }, + extern const struct _mp_obj_module_t mp_module_uarray; + #define MODULE_DEF_MP_QSTR_UARRAY { MP_ROM_QSTR(MP_QSTR_uarray), MP_ROM_PTR(&mp_module_uarray) }, #else - #define MODULE_DEF_MP_QSTR_ARRAY + #define MODULE_DEF_MP_QSTR_UARRAY #endif #define MICROPY_REGISTERED_MODULES \ - MODULE_DEF_MP_QSTR_ARRAY \ + MODULE_DEF_MP_QSTR_UARRAY \ // MICROPY_REGISTERED_MODULES diff --git a/python/port/port.cpp b/python/port/port.cpp index be13fbe7a..12f5f5588 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -46,7 +46,7 @@ bool MicroPython::ExecutionEnvironment::runCode(const char * str) { * to be fed lines and not files. */ // TODO: add a parameter when other input types (file, eval) are required mp_parse_tree_t pt = mp_parse(lex, MP_PARSE_SINGLE_INPUT); - mp_obj_t module_fun = mp_compile(&pt, lex->source_name, MP_EMIT_OPT_NONE, true); + mp_obj_t module_fun = mp_compile(&pt, lex->source_name, true); mp_call_function_0(module_fun); nlr_pop(); } else { // Uncaught exception @@ -71,7 +71,7 @@ bool MicroPython::ExecutionEnvironment::runCode(const char * str) { #endif // the block name can be NULL if it's unknown qstr block = values[i + 2]; - if (block == MP_QSTR_NULL) { + if (block == MP_QSTRnull) { mp_print_str(&mp_plat_print, "\n"); } else { mp_printf(&mp_plat_print, ", in %q\n", block); diff --git a/python/src/extmod/modurandom.c b/python/src/extmod/modurandom.c index 2e667570d..e3c7fc7f5 100644 --- a/python/src/extmod/modurandom.c +++ b/python/src/extmod/modurandom.c @@ -36,8 +36,10 @@ // http://www.literatecode.com/yasmarang // Public Domain +#if !MICROPY_ENABLE_DYNRUNTIME STATIC uint32_t yasmarang_pad = 0xeda4baba, yasmarang_n = 69, yasmarang_d = 233; STATIC uint8_t yasmarang_dat = 0; +#endif STATIC uint32_t yasmarang(void) { @@ -208,6 +210,7 @@ STATIC mp_obj_t mod_urandom___init__() { STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_urandom___init___obj, mod_urandom___init__); #endif +#if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t mp_module_urandom_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_urandom) }, #ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC @@ -232,5 +235,6 @@ const mp_obj_module_t mp_module_urandom = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_urandom_globals, }; +#endif #endif //MICROPY_PY_URANDOM diff --git a/python/src/py/asmarm.c b/python/src/py/asmarm.c index f2221f8a9..59c661cc0 100644 --- a/python/src/py/asmarm.c +++ b/python/src/py/asmarm.c @@ -40,7 +40,11 @@ void asm_arm_end_pass(asm_arm_t *as) { if (as->base.pass == MP_ASM_PASS_EMIT) { -#ifdef __arm__ +#if defined(__linux__) && defined(__GNUC__) + char *start = mp_asm_base_get_code(&as->base); + char *end = start + mp_asm_base_get_code_size(&as->base); + __builtin___clear_cache(start, end); +#elif defined(__arm__) // flush I- and D-cache asm volatile( "0:" diff --git a/python/src/py/asmbase.c b/python/src/py/asmbase.c index 4c84c3b25..ab861da15 100644 --- a/python/src/py/asmbase.c +++ b/python/src/py/asmbase.c @@ -31,7 +31,7 @@ #include "py/misc.h" #include "py/asmbase.h" -#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM +#if MICROPY_EMIT_MACHINE_CODE void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels) { as->max_num_labels = max_num_labels; @@ -99,4 +99,4 @@ void mp_asm_base_data(mp_asm_base_t* as, unsigned int bytesize, uintptr_t val) { } } -#endif // MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM +#endif // MICROPY_EMIT_MACHINE_CODE diff --git a/python/src/py/asmbase.h b/python/src/py/asmbase.h index d2b403893..b5e259358 100644 --- a/python/src/py/asmbase.h +++ b/python/src/py/asmbase.h @@ -60,7 +60,7 @@ static inline size_t mp_asm_base_get_code_size(mp_asm_base_t *as) { static inline void *mp_asm_base_get_code(mp_asm_base_t *as) { #if defined(MP_PLAT_COMMIT_EXEC) - return MP_PLAT_COMMIT_EXEC(as->code_base, as->code_size); + return MP_PLAT_COMMIT_EXEC(as->code_base, as->code_size, NULL); #else return as->code_base; #endif diff --git a/python/src/py/asmxtensa.c b/python/src/py/asmxtensa.c index a269e5e7f..32e5e958a 100644 --- a/python/src/py/asmxtensa.c +++ b/python/src/py/asmxtensa.c @@ -30,14 +30,13 @@ #include "py/mpconfig.h" // wrapper around everything in this file -#if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA +#if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA || MICROPY_EMIT_XTENSAWIN #include "py/asmxtensa.h" #define WORD_SIZE (4) #define SIGNED_FIT8(x) ((((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)) #define SIGNED_FIT12(x) ((((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800)) -#define NUM_REGS_SAVED (5) void asm_xtensa_end_pass(asm_xtensa_t *as) { as->num_const = as->cur_const; @@ -69,7 +68,7 @@ void asm_xtensa_entry(asm_xtensa_t *as, int num_locals) { as->const_table = (uint32_t*)mp_asm_base_get_cur_to_write_bytes(&as->base, as->num_const * 4); // adjust the stack-pointer to store a0, a12, a13, a14, a15 and locals, 16-byte aligned - as->stack_adjust = (((NUM_REGS_SAVED + num_locals) * WORD_SIZE) + 15) & ~15; + as->stack_adjust = (((ASM_XTENSA_NUM_REGS_SAVED + num_locals) * WORD_SIZE) + 15) & ~15; if (SIGNED_FIT8(-as->stack_adjust)) { asm_xtensa_op_addi(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, -as->stack_adjust); } else { @@ -79,14 +78,14 @@ void asm_xtensa_entry(asm_xtensa_t *as, int num_locals) { // save return value (a0) and callee-save registers (a12, a13, a14, a15) asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0); - for (int i = 1; i < NUM_REGS_SAVED; ++i) { + for (int i = 1; i < ASM_XTENSA_NUM_REGS_SAVED; ++i) { asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A11 + i, ASM_XTENSA_REG_A1, i); } } void asm_xtensa_exit(asm_xtensa_t *as) { // restore registers - for (int i = NUM_REGS_SAVED - 1; i >= 1; --i) { + for (int i = ASM_XTENSA_NUM_REGS_SAVED - 1; i >= 1; --i) { asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A11 + i, ASM_XTENSA_REG_A1, i); } asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0); @@ -102,6 +101,22 @@ void asm_xtensa_exit(asm_xtensa_t *as) { asm_xtensa_op_ret_n(as); } +void asm_xtensa_entry_win(asm_xtensa_t *as, int num_locals) { + // jump over the constants + asm_xtensa_op_j(as, as->num_const * WORD_SIZE + 4 - 4); + mp_asm_base_get_cur_to_write_bytes(&as->base, 1); // padding/alignment byte + as->const_table = (uint32_t*)mp_asm_base_get_cur_to_write_bytes(&as->base, as->num_const * 4); + + as->stack_adjust = 32 + ((((ASM_XTENSA_NUM_REGS_SAVED_WIN + num_locals) * WORD_SIZE) + 15) & ~15); + asm_xtensa_op_entry(as, ASM_XTENSA_REG_A1, as->stack_adjust); + asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0); +} + +void asm_xtensa_exit_win(asm_xtensa_t *as) { + asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0); + asm_xtensa_op_retw_n(as); +} + STATIC uint32_t get_label_dest(asm_xtensa_t *as, uint label) { assert(label < as->base.max_num_labels); return as->base.label_offsets[label]; @@ -178,15 +193,15 @@ void asm_xtensa_mov_reg_i32_optimised(asm_xtensa_t *as, uint reg_dest, uint32_t } void asm_xtensa_mov_local_reg(asm_xtensa_t *as, int local_num, uint reg_src) { - asm_xtensa_op_s32i(as, reg_src, ASM_XTENSA_REG_A1, NUM_REGS_SAVED + local_num); + asm_xtensa_op_s32i(as, reg_src, ASM_XTENSA_REG_A1, local_num); } void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num) { - asm_xtensa_op_l32i(as, reg_dest, ASM_XTENSA_REG_A1, NUM_REGS_SAVED + local_num); + asm_xtensa_op_l32i(as, reg_dest, ASM_XTENSA_REG_A1, local_num); } void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num) { - uint off = (NUM_REGS_SAVED + local_num) * WORD_SIZE; + uint off = local_num * WORD_SIZE; if (SIGNED_FIT8(off)) { asm_xtensa_op_addi(as, reg_dest, ASM_XTENSA_REG_A1, off); } else { @@ -226,4 +241,13 @@ void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx) { asm_xtensa_op_callx0(as, ASM_XTENSA_REG_A0); } -#endif // MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA +void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx) { + if (idx < 16) { + asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A8, ASM_XTENSA_REG_FUN_TABLE_WIN, idx); + } else { + asm_xtensa_op_l32i(as, ASM_XTENSA_REG_A8, ASM_XTENSA_REG_FUN_TABLE_WIN, idx); + } + asm_xtensa_op_callx8(as, ASM_XTENSA_REG_A8); +} + +#endif // MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA || MICROPY_EMIT_XTENSAWIN diff --git a/python/src/py/asmxtensa.h b/python/src/py/asmxtensa.h index d95af14a5..5eb40daf7 100644 --- a/python/src/py/asmxtensa.h +++ b/python/src/py/asmxtensa.h @@ -37,6 +37,16 @@ // callee save: a1, a12, a13, a14, a15 // caller save: a3 +// With windowed registers, size 8: +// - a0: return PC +// - a1: stack pointer, full descending, aligned to 16 bytes +// - a2-a7: incoming args, and essentially callee save +// - a2: return value +// - a8-a15: caller save temporaries +// - a10-a15: input args to called function +// - a10: return value of called function +// note: a0-a7 are saved automatically via window shift of called function + #define ASM_XTENSA_REG_A0 (0) #define ASM_XTENSA_REG_A1 (1) #define ASM_XTENSA_REG_A2 (2) @@ -96,6 +106,10 @@ #define ASM_XTENSA_ENCODE_RI7(op0, s, imm7) \ ((((imm7) & 0xf) << 12) | ((s) << 8) | ((imm7) & 0x70) | (op0)) +// Number of registers saved on the stack upon entry to function +#define ASM_XTENSA_NUM_REGS_SAVED (5) +#define ASM_XTENSA_NUM_REGS_SAVED_WIN (1) + typedef struct _asm_xtensa_t { mp_asm_base_t base; uint32_t cur_const; @@ -109,11 +123,18 @@ void asm_xtensa_end_pass(asm_xtensa_t *as); void asm_xtensa_entry(asm_xtensa_t *as, int num_locals); void asm_xtensa_exit(asm_xtensa_t *as); +void asm_xtensa_entry_win(asm_xtensa_t *as, int num_locals); +void asm_xtensa_exit_win(asm_xtensa_t *as); + void asm_xtensa_op16(asm_xtensa_t *as, uint16_t op); void asm_xtensa_op24(asm_xtensa_t *as, uint32_t op); // raw instructions +static inline void asm_xtensa_op_entry(asm_xtensa_t *as, uint reg_src, int32_t num_bytes) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_BRI12(6, reg_src, 0, 3, (num_bytes / 8) & 0xfff)); +} + static inline void asm_xtensa_op_add_n(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(10, reg_dest, reg_src_a, reg_src_b)); } @@ -142,6 +163,10 @@ static inline void asm_xtensa_op_callx0(asm_xtensa_t *as, uint reg) { asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALLX(0, 0, 0, 0, reg, 3, 0)); } +static inline void asm_xtensa_op_callx8(asm_xtensa_t *as, uint reg) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALLX(0, 0, 0, 0, reg, 3, 2)); +} + static inline void asm_xtensa_op_j(asm_xtensa_t *as, int32_t rel18) { asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALL(6, 0, rel18 & 0x3ffff)); } @@ -194,6 +219,10 @@ static inline void asm_xtensa_op_ret_n(asm_xtensa_t *as) { asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(13, 15, 0, 0)); } +static inline void asm_xtensa_op_retw_n(asm_xtensa_t *as) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(13, 15, 0, 1)); +} + static inline void asm_xtensa_op_s8i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint byte_offset) { asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 4, reg_base, reg_src, byte_offset & 0xff)); } @@ -246,9 +275,11 @@ void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num); void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num); void asm_xtensa_mov_reg_pcrel(asm_xtensa_t *as, uint reg_dest, uint label); void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx); +void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); // Holds a pointer to mp_fun_table #define ASM_XTENSA_REG_FUN_TABLE ASM_XTENSA_REG_A15 +#define ASM_XTENSA_REG_FUN_TABLE_WIN ASM_XTENSA_REG_A7 #if GENERIC_ASM_API @@ -257,6 +288,9 @@ void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx); #define ASM_WORD_SIZE (4) +#if !GENERIC_ASM_API_WIN +// Configuration for non-windowed calls + #define REG_RET ASM_XTENSA_REG_A2 #define REG_ARG_1 ASM_XTENSA_REG_A2 #define REG_ARG_2 ASM_XTENSA_REG_A3 @@ -273,12 +307,47 @@ void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx); #define REG_LOCAL_3 ASM_XTENSA_REG_A14 #define REG_LOCAL_NUM (3) +#define ASM_NUM_REGS_SAVED ASM_XTENSA_NUM_REGS_SAVED #define REG_FUN_TABLE ASM_XTENSA_REG_FUN_TABLE +#define ASM_ENTRY(as, nlocal) asm_xtensa_entry((as), (nlocal)) +#define ASM_EXIT(as) asm_xtensa_exit((as)) +#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind((as), (idx)) + +#else +// Configuration for windowed calls with window size 8 + +#define REG_PARENT_RET ASM_XTENSA_REG_A2 +#define REG_PARENT_ARG_1 ASM_XTENSA_REG_A2 +#define REG_PARENT_ARG_2 ASM_XTENSA_REG_A3 +#define REG_PARENT_ARG_3 ASM_XTENSA_REG_A4 +#define REG_PARENT_ARG_4 ASM_XTENSA_REG_A5 +#define REG_RET ASM_XTENSA_REG_A10 +#define REG_ARG_1 ASM_XTENSA_REG_A10 +#define REG_ARG_2 ASM_XTENSA_REG_A11 +#define REG_ARG_3 ASM_XTENSA_REG_A12 +#define REG_ARG_4 ASM_XTENSA_REG_A13 + +#define REG_TEMP0 ASM_XTENSA_REG_A10 +#define REG_TEMP1 ASM_XTENSA_REG_A11 +#define REG_TEMP2 ASM_XTENSA_REG_A12 + +#define REG_LOCAL_1 ASM_XTENSA_REG_A4 +#define REG_LOCAL_2 ASM_XTENSA_REG_A5 +#define REG_LOCAL_3 ASM_XTENSA_REG_A6 +#define REG_LOCAL_NUM (3) + +#define ASM_NUM_REGS_SAVED ASM_XTENSA_NUM_REGS_SAVED_WIN +#define REG_FUN_TABLE ASM_XTENSA_REG_FUN_TABLE_WIN + +#define ASM_ENTRY(as, nlocal) asm_xtensa_entry_win((as), (nlocal)) +#define ASM_EXIT(as) asm_xtensa_exit_win((as)) +#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind_win((as), (idx)) + +#endif + #define ASM_T asm_xtensa_t #define ASM_END_PASS asm_xtensa_end_pass -#define ASM_ENTRY asm_xtensa_entry -#define ASM_EXIT asm_xtensa_exit #define ASM_JUMP asm_xtensa_j_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ @@ -288,15 +357,14 @@ void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx); #define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ asm_xtensa_bcc_reg_reg_label(as, ASM_XTENSA_CC_EQ, reg1, reg2, label) #define ASM_JUMP_REG(as, reg) asm_xtensa_op_jx((as), (reg)) -#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind((as), (idx)) -#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_xtensa_mov_local_reg((as), (local_num), (reg_src)) +#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_xtensa_mov_local_reg((as), ASM_NUM_REGS_SAVED + (local_num), (reg_src)) #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_xtensa_mov_reg_i32_optimised((as), (reg_dest), (imm)) #define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm)) #define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm)) -#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_xtensa_mov_reg_local((as), (reg_dest), (local_num)) +#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_xtensa_mov_reg_local((as), (reg_dest), ASM_NUM_REGS_SAVED + (local_num)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mov_n((as), (reg_dest), (reg_src)) -#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_xtensa_mov_reg_local_addr((as), (reg_dest), (local_num)) +#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_xtensa_mov_reg_local_addr((as), (reg_dest), ASM_NUM_REGS_SAVED + (local_num)) #define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_xtensa_mov_reg_pcrel((as), (reg_dest), (label)) #define ASM_LSL_REG_REG(as, reg_dest, reg_shift) \ diff --git a/python/src/py/bc.c b/python/src/py/bc.c index b178e7c20..7dd4b2246 100644 --- a/python/src/py/bc.c +++ b/python/src/py/bc.c @@ -40,6 +40,8 @@ #define DEBUG_printf(...) (void)0 #endif +#if !MICROPY_PERSISTENT_CODE + mp_uint_t mp_decode_uint(const byte **ptr) { mp_uint_t unum = 0; byte val; @@ -70,6 +72,8 @@ const byte *mp_decode_uint_skip(const byte *ptr) { return ptr; } +#endif + STATIC NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) { #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE // generic message, used also for other argument issues @@ -119,16 +123,22 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw code_state->prev = NULL; #endif - // get params - size_t n_state = mp_decode_uint(&code_state->ip); - code_state->ip = mp_decode_uint_skip(code_state->ip); // skip n_exc_stack - size_t scope_flags = *code_state->ip++; - size_t n_pos_args = *code_state->ip++; - size_t n_kwonly_args = *code_state->ip++; - size_t n_def_pos_args = *code_state->ip++; + #if MICROPY_PY_SYS_SETTRACE + code_state->prev_state = NULL; + code_state->frame = NULL; + #endif + + // Get cached n_state (rather than decode it again) + size_t n_state = code_state->n_state; + + // Decode prelude + size_t n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args; + MP_BC_PRELUDE_SIG_DECODE_INTO(code_state->ip, n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args); + (void)n_state_unused; + (void)n_exc_stack_unused; code_state->sp = &code_state->state[0] - 1; - code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1; + code_state->exc_sp_idx = 0; // zero out the local stack to begin with memset(code_state->state, 0, n_state * sizeof(*code_state->state)); @@ -263,19 +273,25 @@ continue2:; } } - // get the ip and skip argument names + // read the size part of the prelude const byte *ip = code_state->ip; + MP_BC_PRELUDE_SIZE_DECODE(ip); // jump over code info (source file and line-number mapping) - ip += mp_decode_uint_value(ip); + ip += n_info; // bytecode prelude: initialise closed over variables - size_t local_num; - while ((local_num = *ip++) != 255) { + for (; n_cell; --n_cell) { + size_t local_num = *ip++; code_state->state[n_state - 1 - local_num] = mp_obj_new_cell(code_state->state[n_state - 1 - local_num]); } + #if !MICROPY_PERSISTENT_CODE + // so bytecode is aligned + ip = MP_ALIGN(ip, sizeof(mp_uint_t)); + #endif + // now that we skipped over the prelude, set the ip for the VM code_state->ip = ip; @@ -287,105 +303,17 @@ continue2:; #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE // The following table encodes the number of bytes that a specific opcode -// takes up. There are 3 special opcodes that always have an extra byte: -// MP_BC_MAKE_CLOSURE -// MP_BC_MAKE_CLOSURE_DEFARGS -// MP_BC_RAISE_VARARGS +// takes up. Some opcodes have an extra byte, defined by MP_BC_MASK_EXTRA_BYTE. // There are 4 special opcodes that have an extra byte only when // MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE is enabled (and they take a qstr): // MP_BC_LOAD_NAME // MP_BC_LOAD_GLOBAL // MP_BC_LOAD_ATTR // MP_BC_STORE_ATTR -#define OC4(a, b, c, d) (a | (b << 2) | (c << 4) | (d << 6)) -#define U (0) // undefined opcode -#define B (MP_OPCODE_BYTE) // single byte -#define Q (MP_OPCODE_QSTR) // single byte plus 2-byte qstr -#define V (MP_OPCODE_VAR_UINT) // single byte plus variable encoded unsigned int -#define O (MP_OPCODE_OFFSET) // single byte plus 2-byte bytecode offset -STATIC const byte opcode_format_table[64] = { - OC4(U, U, U, U), // 0x00-0x03 - OC4(U, U, U, U), // 0x04-0x07 - OC4(U, U, U, U), // 0x08-0x0b - OC4(U, U, U, U), // 0x0c-0x0f - OC4(B, B, B, U), // 0x10-0x13 - OC4(V, U, Q, V), // 0x14-0x17 - OC4(B, V, V, Q), // 0x18-0x1b - OC4(Q, Q, Q, Q), // 0x1c-0x1f - OC4(B, B, V, V), // 0x20-0x23 - OC4(Q, Q, Q, B), // 0x24-0x27 - OC4(V, V, Q, Q), // 0x28-0x2b - OC4(U, U, U, U), // 0x2c-0x2f - OC4(B, B, B, B), // 0x30-0x33 - OC4(B, O, O, O), // 0x34-0x37 - OC4(O, O, U, U), // 0x38-0x3b - OC4(U, O, B, O), // 0x3c-0x3f - OC4(O, B, B, O), // 0x40-0x43 - OC4(O, U, O, B), // 0x44-0x47 - OC4(U, U, U, U), // 0x48-0x4b - OC4(U, U, U, U), // 0x4c-0x4f - OC4(V, V, U, V), // 0x50-0x53 - OC4(B, U, V, V), // 0x54-0x57 - OC4(V, V, V, B), // 0x58-0x5b - OC4(B, B, B, U), // 0x5c-0x5f - OC4(V, V, V, V), // 0x60-0x63 - OC4(V, V, V, V), // 0x64-0x67 - OC4(Q, Q, B, U), // 0x68-0x6b - OC4(U, U, U, U), // 0x6c-0x6f - - OC4(B, B, B, B), // 0x70-0x73 - OC4(B, B, B, B), // 0x74-0x77 - OC4(B, B, B, B), // 0x78-0x7b - OC4(B, B, B, B), // 0x7c-0x7f - OC4(B, B, B, B), // 0x80-0x83 - OC4(B, B, B, B), // 0x84-0x87 - OC4(B, B, B, B), // 0x88-0x8b - OC4(B, B, B, B), // 0x8c-0x8f - OC4(B, B, B, B), // 0x90-0x93 - OC4(B, B, B, B), // 0x94-0x97 - OC4(B, B, B, B), // 0x98-0x9b - OC4(B, B, B, B), // 0x9c-0x9f - OC4(B, B, B, B), // 0xa0-0xa3 - OC4(B, B, B, B), // 0xa4-0xa7 - OC4(B, B, B, B), // 0xa8-0xab - OC4(B, B, B, B), // 0xac-0xaf - - OC4(B, B, B, B), // 0xb0-0xb3 - OC4(B, B, B, B), // 0xb4-0xb7 - OC4(B, B, B, B), // 0xb8-0xbb - OC4(B, B, B, B), // 0xbc-0xbf - - OC4(B, B, B, B), // 0xc0-0xc3 - OC4(B, B, B, B), // 0xc4-0xc7 - OC4(B, B, B, B), // 0xc8-0xcb - OC4(B, B, B, B), // 0xcc-0xcf - - OC4(B, B, B, B), // 0xd0-0xd3 - OC4(U, U, U, B), // 0xd4-0xd7 - OC4(B, B, B, B), // 0xd8-0xdb - OC4(B, B, B, B), // 0xdc-0xdf - - OC4(B, B, B, B), // 0xe0-0xe3 - OC4(B, B, B, B), // 0xe4-0xe7 - OC4(B, B, B, B), // 0xe8-0xeb - OC4(B, B, B, B), // 0xec-0xef - - OC4(B, B, B, B), // 0xf0-0xf3 - OC4(B, B, B, B), // 0xf4-0xf7 - OC4(U, U, U, U), // 0xf8-0xfb - OC4(U, U, U, U), // 0xfc-0xff -}; -#undef OC4 -#undef U -#undef B -#undef Q -#undef V -#undef O - uint mp_opcode_format(const byte *ip, size_t *opcode_size, bool count_var_uint) { - uint f = (opcode_format_table[*ip >> 2] >> (2 * (*ip & 3))) & 3; + uint f = MP_BC_FORMAT(*ip); const byte *ip_start = ip; - if (f == MP_OPCODE_QSTR) { + if (f == MP_BC_FORMAT_QSTR) { if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) { if (*ip == MP_BC_LOAD_NAME || *ip == MP_BC_LOAD_GLOBAL @@ -396,18 +324,14 @@ uint mp_opcode_format(const byte *ip, size_t *opcode_size, bool count_var_uint) } ip += 3; } else { - int extra_byte = ( - *ip == MP_BC_RAISE_VARARGS - || *ip == MP_BC_MAKE_CLOSURE - || *ip == MP_BC_MAKE_CLOSURE_DEFARGS - ); + int extra_byte = (*ip & MP_BC_MASK_EXTRA_BYTE) == 0; ip += 1; - if (f == MP_OPCODE_VAR_UINT) { + if (f == MP_BC_FORMAT_VAR_UINT) { if (count_var_uint) { while ((*ip++ & 0x80) != 0) { } } - } else if (f == MP_OPCODE_OFFSET) { + } else if (f == MP_BC_FORMAT_OFFSET) { ip += 2; } ip += extra_byte; diff --git a/python/src/py/bc.h b/python/src/py/bc.h index 0aadfa8a3..a96d17a0d 100644 --- a/python/src/py/bc.h +++ b/python/src/py/bc.h @@ -32,24 +32,35 @@ // bytecode layout: // -// n_state : var uint -// n_exc_stack : var uint -// scope_flags : byte -// n_pos_args : byte number of arguments this function takes -// n_kwonly_args : byte number of keyword-only arguments this function takes -// n_def_pos_args : byte number of default positional arguments +// func signature : var uint +// contains six values interleaved bit-wise as: xSSSSEAA [xFSSKAED repeated] +// x = extension another byte follows +// S = n_state - 1 number of entries in Python value stack +// E = n_exc_stack number of entries in exception stack +// F = scope_flags four bits of flags, MP_SCOPE_FLAG_xxx +// A = n_pos_args number of arguments this function takes +// K = n_kwonly_args number of keyword-only arguments this function takes +// D = n_def_pos_args number of default positional arguments // -// code_info_size : var uint | code_info_size counts bytes in this chunk -// simple_name : var qstr | -// source_file : var qstr | -// | -// | only needed if bytecode contains pointers +// prelude size : var uint +// contains two values interleaved bit-wise as: xIIIIIIC repeated +// x = extension another byte follows +// I = n_info number of bytes in source info section +// C = n_cells number of bytes/cells in closure section // -// local_num0 : byte | -// ... : byte | -// local_numN : byte | N = num_cells -// 255 : byte | end of list sentinel -// | +// source info section: +// simple_name : var qstr +// source_file : var qstr +// +// +// closure section: +// local_num0 : byte +// ... : byte +// local_numN : byte N = n_cells-1 +// +// only needed if bytecode contains pointers +// +// // // // constant table layout: @@ -60,13 +71,127 @@ // const0 : obj // constN : obj +#define MP_BC_PRELUDE_SIG_ENCODE(S, E, scope, out_byte, out_env) \ +do { \ + /*// Get values to store in prelude */ \ + size_t F = scope->scope_flags & MP_SCOPE_FLAG_ALL_SIG; \ + size_t A = scope->num_pos_args; \ + size_t K = scope->num_kwonly_args; \ + size_t D = scope->num_def_pos_args; \ + \ + /* Adjust S to shrink range, to compress better */ \ + S -= 1; \ + \ + /* Encode prelude */ \ + /* xSSSSEAA */ \ + uint8_t z = (S & 0xf) << 3 | (E & 1) << 2 | (A & 3); \ + S >>= 4; \ + E >>= 1; \ + A >>= 2; \ + while (S | E | F | A | K | D) { \ + out_byte(out_env, 0x80 | z); \ + /* xFSSKAED */ \ + z = (F & 1) << 6 | (S & 3) << 4 | (K & 1) << 3 \ + | (A & 1) << 2 | (E & 1) << 1 | (D & 1); \ + S >>= 2; \ + E >>= 1; \ + F >>= 1; \ + A >>= 1; \ + K >>= 1; \ + D >>= 1; \ + } \ + out_byte(out_env, z); \ +} while (0) + +#define MP_BC_PRELUDE_SIG_DECODE_INTO(ip, S, E, F, A, K, D) \ +do { \ + uint8_t z = *(ip)++; \ + /* xSSSSEAA */ \ + S = (z >> 3) & 0xf; \ + E = (z >> 2) & 0x1; \ + F = 0; \ + A = z & 0x3; \ + K = 0; \ + D = 0; \ + for (unsigned n = 0; z & 0x80; ++n) { \ + z = *(ip)++; \ + /* xFSSKAED */ \ + S |= (z & 0x30) << (2 * n); \ + E |= (z & 0x02) << n; \ + F |= ((z & 0x40) >> 6) << n; \ + A |= (z & 0x4) << n; \ + K |= ((z & 0x08) >> 3) << n; \ + D |= (z & 0x1) << n; \ + } \ + S += 1; \ +} while (0) + +#define MP_BC_PRELUDE_SIG_DECODE(ip) \ + size_t n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args; \ + MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args) + +#define MP_BC_PRELUDE_SIZE_ENCODE(I, C, out_byte, out_env) \ +do { \ + /* Encode bit-wise as: xIIIIIIC */ \ + uint8_t z = 0; \ + do { \ + z = (I & 0x3f) << 1 | (C & 1); \ + C >>= 1; \ + I >>= 6; \ + if (C | I) { \ + z |= 0x80; \ + } \ + out_byte(out_env, z); \ + } while (C | I); \ +} while (0) + +#define MP_BC_PRELUDE_SIZE_DECODE_INTO(ip, I, C) \ +do { \ + uint8_t z; \ + C = 0; \ + I = 0; \ + for (unsigned n = 0;; ++n) { \ + z = *(ip)++; \ + /* xIIIIIIC */ \ + C |= (z & 1) << n; \ + I |= ((z & 0x7e) >> 1) << (6 * n); \ + if (!(z & 0x80)) { \ + break; \ + } \ + } \ +} while (0) + +#define MP_BC_PRELUDE_SIZE_DECODE(ip) \ + size_t n_info, n_cell; \ + MP_BC_PRELUDE_SIZE_DECODE_INTO(ip, n_info, n_cell) + +// Sentinel value for mp_code_state_t.exc_sp_idx +#define MP_CODE_STATE_EXC_SP_IDX_SENTINEL ((uint16_t)-1) + +// To convert mp_code_state_t.exc_sp_idx to/from a pointer to mp_exc_stack_t +#define MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp) ((exc_sp) + 1 - (exc_stack)) +#define MP_CODE_STATE_EXC_SP_IDX_TO_PTR(exc_stack, exc_sp_idx) ((exc_stack) + (exc_sp_idx) - 1) + +typedef struct _mp_bytecode_prelude_t { + uint n_state; + uint n_exc_stack; + uint scope_flags; + uint n_pos_args; + uint n_kwonly_args; + uint n_def_pos_args; + qstr qstr_block_name; + qstr qstr_source_file; + const byte *line_info; + const byte *opcodes; +} mp_bytecode_prelude_t; + // Exception stack entry typedef struct _mp_exc_stack_t { const byte *handler; - // bit 0 is saved currently_in_except_block value + // bit 0 is currently unused // bit 1 is whether the opcode was SETUP_WITH or SETUP_FINALLY mp_obj_t *val_sp; - // Saved exception, valid if currently_in_except_block bit is 1 + // Saved exception mp_obj_base_t *prev_exc; } mp_exc_stack_t; @@ -78,12 +203,16 @@ typedef struct _mp_code_state_t { mp_obj_fun_bc_t *fun_bc; const byte *ip; mp_obj_t *sp; - // bit 0 is saved currently_in_except_block value - mp_exc_stack_t *exc_sp; + uint16_t n_state; + uint16_t exc_sp_idx; mp_obj_dict_t *old_globals; #if MICROPY_STACKLESS struct _mp_code_state_t *prev; #endif + #if MICROPY_PY_SYS_SETTRACE + struct _mp_code_state_t *prev_state; + struct _mp_obj_frame_t *frame; + #endif // Variable-length mp_obj_t state[0]; // Variable-length, never accessed by name, only as (void*)(state + n_state) @@ -110,13 +239,35 @@ const byte *mp_bytecode_print_str(const byte *ip); #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE -#define MP_OPCODE_BYTE (0) -#define MP_OPCODE_QSTR (1) -#define MP_OPCODE_VAR_UINT (2) -#define MP_OPCODE_OFFSET (3) - uint mp_opcode_format(const byte *ip, size_t *opcode_size, bool count_var_uint); #endif +static inline size_t mp_bytecode_get_source_line(const byte *line_info, size_t bc_offset) { + size_t source_line = 1; + size_t c; + while ((c = *line_info)) { + size_t b, l; + if ((c & 0x80) == 0) { + // 0b0LLBBBBB encoding + b = c & 0x1f; + l = c >> 5; + line_info += 1; + } else { + // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) + b = c & 0xf; + l = ((c << 4) & 0x700) | line_info[1]; + line_info += 2; + } + if (bc_offset >= b) { + bc_offset -= b; + source_line += l; + } else { + // found source line corresponding to bytecode offset + break; + } + } + return source_line; +} + #endif // MICROPY_INCLUDED_PY_BC_H diff --git a/python/src/py/bc0.h b/python/src/py/bc0.h index 175ee263a..7cf061fc4 100644 --- a/python/src/py/bc0.h +++ b/python/src/py/bc0.h @@ -26,93 +26,125 @@ #ifndef MICROPY_INCLUDED_PY_BC0_H #define MICROPY_INCLUDED_PY_BC0_H -// MicroPython byte-codes. -// The comment at the end of the line (if it exists) tells the arguments to the byte-code. +// MicroPython bytecode opcodes, grouped based on the format of the opcode -#define MP_BC_LOAD_CONST_FALSE (0x10) -#define MP_BC_LOAD_CONST_NONE (0x11) -#define MP_BC_LOAD_CONST_TRUE (0x12) -#define MP_BC_LOAD_CONST_SMALL_INT (0x14) // signed var-int -#define MP_BC_LOAD_CONST_STRING (0x16) // qstr -#define MP_BC_LOAD_CONST_OBJ (0x17) // ptr -#define MP_BC_LOAD_NULL (0x18) +#define MP_BC_MASK_FORMAT (0xf0) +#define MP_BC_MASK_EXTRA_BYTE (0x9e) -#define MP_BC_LOAD_FAST_N (0x19) // uint -#define MP_BC_LOAD_DEREF (0x1a) // uint -#define MP_BC_LOAD_NAME (0x1b) // qstr -#define MP_BC_LOAD_GLOBAL (0x1c) // qstr -#define MP_BC_LOAD_ATTR (0x1d) // qstr -#define MP_BC_LOAD_METHOD (0x1e) // qstr -#define MP_BC_LOAD_SUPER_METHOD (0x1f) // qstr -#define MP_BC_LOAD_BUILD_CLASS (0x20) -#define MP_BC_LOAD_SUBSCR (0x21) +#define MP_BC_FORMAT_BYTE (0) +#define MP_BC_FORMAT_QSTR (1) +#define MP_BC_FORMAT_VAR_UINT (2) +#define MP_BC_FORMAT_OFFSET (3) -#define MP_BC_STORE_FAST_N (0x22) // uint -#define MP_BC_STORE_DEREF (0x23) // uint -#define MP_BC_STORE_NAME (0x24) // qstr -#define MP_BC_STORE_GLOBAL (0x25) // qstr -#define MP_BC_STORE_ATTR (0x26) // qstr -#define MP_BC_STORE_SUBSCR (0x27) +// Nibbles in magic number are: BB BB BB BB BB BO VV QU +#define MP_BC_FORMAT(op) ((0x000003a4 >> (2 * ((op) >> 4))) & 3) -#define MP_BC_DELETE_FAST (0x28) // uint -#define MP_BC_DELETE_DEREF (0x29) // uint -#define MP_BC_DELETE_NAME (0x2a) // qstr -#define MP_BC_DELETE_GLOBAL (0x2b) // qstr +// Load, Store, Delete, Import, Make, Build, Unpack, Call, Jump, Exception, For, sTack, Return, Yield, Op +#define MP_BC_BASE_RESERVED (0x00) // ---------------- +#define MP_BC_BASE_QSTR_O (0x10) // LLLLLLSSSDDII--- +#define MP_BC_BASE_VINT_E (0x20) // MMLLLLSSDDBBBBBB +#define MP_BC_BASE_VINT_O (0x30) // UUMMCCCC-------- +#define MP_BC_BASE_JUMP_E (0x40) // J-JJJJJEEEEF---- +#define MP_BC_BASE_BYTE_O (0x50) // LLLLSSDTTTTTEEFF +#define MP_BC_BASE_BYTE_E (0x60) // --BREEEYYI------ +#define MP_BC_LOAD_CONST_SMALL_INT_MULTI (0x70) // LLLLLLLLLLLLLLLL + // (0x80) // LLLLLLLLLLLLLLLL + // (0x90) // LLLLLLLLLLLLLLLL + // (0xa0) // LLLLLLLLLLLLLLLL +#define MP_BC_LOAD_FAST_MULTI (0xb0) // LLLLLLLLLLLLLLLL +#define MP_BC_STORE_FAST_MULTI (0xc0) // SSSSSSSSSSSSSSSS +#define MP_BC_UNARY_OP_MULTI (0xd0) // OOOOOOO +#define MP_BC_BINARY_OP_MULTI (0xd7) // OOOOOOOOO + // (0xe0) // OOOOOOOOOOOOOOOO + // (0xf0) // OOOOOOOOOO------ -#define MP_BC_DUP_TOP (0x30) -#define MP_BC_DUP_TOP_TWO (0x31) -#define MP_BC_POP_TOP (0x32) -#define MP_BC_ROT_TWO (0x33) -#define MP_BC_ROT_THREE (0x34) +#define MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM (64) +#define MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS (16) +#define MP_BC_LOAD_FAST_MULTI_NUM (16) +#define MP_BC_STORE_FAST_MULTI_NUM (16) +#define MP_BC_UNARY_OP_MULTI_NUM (MP_UNARY_OP_NUM_BYTECODE) +#define MP_BC_BINARY_OP_MULTI_NUM (MP_BINARY_OP_NUM_BYTECODE) -#define MP_BC_JUMP (0x35) // rel byte code offset, 16-bit signed, in excess -#define MP_BC_POP_JUMP_IF_TRUE (0x36) // rel byte code offset, 16-bit signed, in excess -#define MP_BC_POP_JUMP_IF_FALSE (0x37) // rel byte code offset, 16-bit signed, in excess -#define MP_BC_JUMP_IF_TRUE_OR_POP (0x38) // rel byte code offset, 16-bit signed, in excess -#define MP_BC_JUMP_IF_FALSE_OR_POP (0x39) // rel byte code offset, 16-bit signed, in excess -#define MP_BC_SETUP_WITH (0x3d) // rel byte code offset, 16-bit unsigned -#define MP_BC_WITH_CLEANUP (0x3e) -#define MP_BC_SETUP_EXCEPT (0x3f) // rel byte code offset, 16-bit unsigned -#define MP_BC_SETUP_FINALLY (0x40) // rel byte code offset, 16-bit unsigned -#define MP_BC_END_FINALLY (0x41) -#define MP_BC_GET_ITER (0x42) -#define MP_BC_FOR_ITER (0x43) // rel byte code offset, 16-bit unsigned -#define MP_BC_POP_EXCEPT_JUMP (0x44) // rel byte code offset, 16-bit unsigned -#define MP_BC_UNWIND_JUMP (0x46) // rel byte code offset, 16-bit signed, in excess; then a byte -#define MP_BC_GET_ITER_STACK (0x47) +#define MP_BC_LOAD_CONST_FALSE (MP_BC_BASE_BYTE_O + 0x00) +#define MP_BC_LOAD_CONST_NONE (MP_BC_BASE_BYTE_O + 0x01) +#define MP_BC_LOAD_CONST_TRUE (MP_BC_BASE_BYTE_O + 0x02) +#define MP_BC_LOAD_CONST_SMALL_INT (MP_BC_BASE_VINT_E + 0x02) // signed var-int +#define MP_BC_LOAD_CONST_STRING (MP_BC_BASE_QSTR_O + 0x00) // qstr +#define MP_BC_LOAD_CONST_OBJ (MP_BC_BASE_VINT_E + 0x03) // ptr +#define MP_BC_LOAD_NULL (MP_BC_BASE_BYTE_O + 0x03) -#define MP_BC_BUILD_TUPLE (0x50) // uint -#define MP_BC_BUILD_LIST (0x51) // uint -#define MP_BC_BUILD_MAP (0x53) // uint -#define MP_BC_STORE_MAP (0x54) -#define MP_BC_BUILD_SET (0x56) // uint -#define MP_BC_BUILD_SLICE (0x58) // uint -#define MP_BC_STORE_COMP (0x57) // uint -#define MP_BC_UNPACK_SEQUENCE (0x59) // uint -#define MP_BC_UNPACK_EX (0x5a) // uint +#define MP_BC_LOAD_FAST_N (MP_BC_BASE_VINT_E + 0x04) // uint +#define MP_BC_LOAD_DEREF (MP_BC_BASE_VINT_E + 0x05) // uint +#define MP_BC_LOAD_NAME (MP_BC_BASE_QSTR_O + 0x01) // qstr +#define MP_BC_LOAD_GLOBAL (MP_BC_BASE_QSTR_O + 0x02) // qstr +#define MP_BC_LOAD_ATTR (MP_BC_BASE_QSTR_O + 0x03) // qstr +#define MP_BC_LOAD_METHOD (MP_BC_BASE_QSTR_O + 0x04) // qstr +#define MP_BC_LOAD_SUPER_METHOD (MP_BC_BASE_QSTR_O + 0x05) // qstr +#define MP_BC_LOAD_BUILD_CLASS (MP_BC_BASE_BYTE_O + 0x04) +#define MP_BC_LOAD_SUBSCR (MP_BC_BASE_BYTE_O + 0x05) -#define MP_BC_RETURN_VALUE (0x5b) -#define MP_BC_RAISE_VARARGS (0x5c) // byte -#define MP_BC_YIELD_VALUE (0x5d) -#define MP_BC_YIELD_FROM (0x5e) +#define MP_BC_STORE_FAST_N (MP_BC_BASE_VINT_E + 0x06) // uint +#define MP_BC_STORE_DEREF (MP_BC_BASE_VINT_E + 0x07) // uint +#define MP_BC_STORE_NAME (MP_BC_BASE_QSTR_O + 0x06) // qstr +#define MP_BC_STORE_GLOBAL (MP_BC_BASE_QSTR_O + 0x07) // qstr +#define MP_BC_STORE_ATTR (MP_BC_BASE_QSTR_O + 0x08) // qstr +#define MP_BC_STORE_SUBSCR (MP_BC_BASE_BYTE_O + 0x06) -#define MP_BC_MAKE_FUNCTION (0x60) // uint -#define MP_BC_MAKE_FUNCTION_DEFARGS (0x61) // uint -#define MP_BC_MAKE_CLOSURE (0x62) // uint -#define MP_BC_MAKE_CLOSURE_DEFARGS (0x63) // uint -#define MP_BC_CALL_FUNCTION (0x64) // uint -#define MP_BC_CALL_FUNCTION_VAR_KW (0x65) // uint -#define MP_BC_CALL_METHOD (0x66) // uint -#define MP_BC_CALL_METHOD_VAR_KW (0x67) // uint +#define MP_BC_DELETE_FAST (MP_BC_BASE_VINT_E + 0x08) // uint +#define MP_BC_DELETE_DEREF (MP_BC_BASE_VINT_E + 0x09) // uint +#define MP_BC_DELETE_NAME (MP_BC_BASE_QSTR_O + 0x09) // qstr +#define MP_BC_DELETE_GLOBAL (MP_BC_BASE_QSTR_O + 0x0a) // qstr -#define MP_BC_IMPORT_NAME (0x68) // qstr -#define MP_BC_IMPORT_FROM (0x69) // qstr -#define MP_BC_IMPORT_STAR (0x6a) +#define MP_BC_DUP_TOP (MP_BC_BASE_BYTE_O + 0x07) +#define MP_BC_DUP_TOP_TWO (MP_BC_BASE_BYTE_O + 0x08) +#define MP_BC_POP_TOP (MP_BC_BASE_BYTE_O + 0x09) +#define MP_BC_ROT_TWO (MP_BC_BASE_BYTE_O + 0x0a) +#define MP_BC_ROT_THREE (MP_BC_BASE_BYTE_O + 0x0b) -#define MP_BC_LOAD_CONST_SMALL_INT_MULTI (0x70) // + N(64) -#define MP_BC_LOAD_FAST_MULTI (0xb0) // + N(16) -#define MP_BC_STORE_FAST_MULTI (0xc0) // + N(16) -#define MP_BC_UNARY_OP_MULTI (0xd0) // + op( 'Z') -mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr) { +mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte **ptr) { byte *p = *ptr; - mp_uint_t align; + size_t align; size_t size = mp_binary_get_size(struct_type, val_type, &align); if (struct_type == '@') { - // Make pointer aligned - p = (byte*)MP_ALIGN(p, (size_t)align); + // Align p relative to p_base + p = p_base + (uintptr_t)MP_ALIGN(p - p_base, align); #if MP_ENDIANNESS_LITTLE struct_type = '<'; #else @@ -231,7 +231,7 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr) { } } -void mp_binary_set_int(mp_uint_t val_sz, bool big_endian, byte *dest, mp_uint_t val) { +void mp_binary_set_int(size_t val_sz, bool big_endian, byte *dest, mp_uint_t val) { if (MP_ENDIANNESS_LITTLE && !big_endian) { memcpy(dest, &val, val_sz); } else if (MP_ENDIANNESS_BIG && big_endian) { @@ -250,14 +250,14 @@ void mp_binary_set_int(mp_uint_t val_sz, bool big_endian, byte *dest, mp_uint_t } } -void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte **ptr) { +void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p_base, byte **ptr) { byte *p = *ptr; - mp_uint_t align; + size_t align; size_t size = mp_binary_get_size(struct_type, val_type, &align); if (struct_type == '@') { - // Make pointer aligned - p = (byte*)MP_ALIGN(p, (size_t)align); + // Align p relative to p_base + p = p_base + (uintptr_t)MP_ALIGN(p - p_base, align); if (MP_ENDIANNESS_LITTLE) { struct_type = '<'; } else { @@ -315,7 +315,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte ** mp_binary_set_int(MIN((size_t)size, sizeof(val)), struct_type == '>', p, val); } -void mp_binary_set_val_array(char typecode, void *p, mp_uint_t index, mp_obj_t val_in) { +void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_in) { switch (typecode) { #if MICROPY_PY_BUILTINS_FLOAT case 'f': @@ -342,7 +342,7 @@ void mp_binary_set_val_array(char typecode, void *p, mp_uint_t index, mp_obj_t v } } -void mp_binary_set_val_array_from_int(char typecode, void *p, mp_uint_t index, mp_int_t val) { +void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_int_t val) { switch (typecode) { case 'b': ((signed char*)p)[index] = val; diff --git a/python/src/py/binary.h b/python/src/py/binary.h index 71182042f..5c645bcaa 100644 --- a/python/src/py/binary.h +++ b/python/src/py/binary.h @@ -34,13 +34,13 @@ // type-specification errors due to end-of-string. #define BYTEARRAY_TYPECODE 1 -size_t mp_binary_get_size(char struct_type, char val_type, mp_uint_t *palign); -mp_obj_t mp_binary_get_val_array(char typecode, void *p, mp_uint_t index); -void mp_binary_set_val_array(char typecode, void *p, mp_uint_t index, mp_obj_t val_in); -void mp_binary_set_val_array_from_int(char typecode, void *p, mp_uint_t index, mp_int_t val); -mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr); -void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte **ptr); -long long mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, const byte *src); -void mp_binary_set_int(mp_uint_t val_sz, bool big_endian, byte *dest, mp_uint_t val); +size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign); +mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index); +void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_in); +void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_int_t val); +mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte **ptr); +void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p_base, byte **ptr); +long long mp_binary_get_int(size_t size, bool is_signed, bool big_endian, const byte *src); +void mp_binary_set_int(size_t val_sz, bool big_endian, byte *dest, mp_uint_t val); #endif // MICROPY_INCLUDED_PY_BINARY_H diff --git a/python/src/py/builtin.h b/python/src/py/builtin.h index a5e0f5f2d..2dbe8a782 100644 --- a/python/src/py/builtin.h +++ b/python/src/py/builtin.h @@ -89,7 +89,7 @@ MP_DECLARE_CONST_FUN_OBJ_2(mp_op_delitem_obj); extern const mp_obj_module_t mp_module___main__; extern const mp_obj_module_t mp_module_builtins; -extern const mp_obj_module_t mp_module_array; +extern const mp_obj_module_t mp_module_uarray; extern const mp_obj_module_t mp_module_collections; extern const mp_obj_module_t mp_module_io; extern const mp_obj_module_t mp_module_math; @@ -122,6 +122,7 @@ extern const mp_obj_module_t mp_module_uwebsocket; extern const mp_obj_module_t mp_module_webrepl; extern const mp_obj_module_t mp_module_framebuf; extern const mp_obj_module_t mp_module_btree; +extern const mp_obj_module_t mp_module_ubluetooth; extern const char MICROPY_PY_BUILTINS_HELP_TEXT[]; diff --git a/python/src/py/builtinhelp.c b/python/src/py/builtinhelp.c index a7fede00a..8f162d885 100644 --- a/python/src/py/builtinhelp.c +++ b/python/src/py/builtinhelp.c @@ -80,10 +80,6 @@ STATIC void mp_help_print_modules(void) { mp_help_add_from_map(list, &mp_builtin_module_map); - #if MICROPY_MODULE_WEAK_LINKS - mp_help_add_from_map(list, &mp_builtin_module_weak_links_map); - #endif - #if MICROPY_MODULE_FROZEN_STR extern const char mp_frozen_str_names[]; mp_help_add_from_names(list, mp_frozen_str_names); diff --git a/python/src/py/builtinimport.c b/python/src/py/builtinimport.c index bb88f2ace..9d91b2059 100644 --- a/python/src/py/builtinimport.c +++ b/python/src/py/builtinimport.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013-2019 Damien P. George * Copyright (c) 2014 Paul Sokolovsky * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -145,11 +145,11 @@ STATIC void do_load_from_lexer(mp_obj_t module_obj, mp_lexer_t *lex) { #endif #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_MODULE_FROZEN_MPY -STATIC void do_execute_raw_code(mp_obj_t module_obj, mp_raw_code_t *raw_code) { +STATIC void do_execute_raw_code(mp_obj_t module_obj, mp_raw_code_t *raw_code, const char* source_name) { + (void)source_name; + #if MICROPY_PY___FILE__ - // TODO - //qstr source_name = lex->source_name; - //mp_store_attr(module_obj, MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); + mp_store_attr(module_obj, MP_QSTR___file__, MP_OBJ_NEW_QSTR(qstr_from_str(source_name))); #endif // execute the module in its context @@ -206,7 +206,7 @@ STATIC void do_load(mp_obj_t module_obj, vstr_t *file) { // its data) in the list of frozen files, execute it. #if MICROPY_MODULE_FROZEN_MPY if (frozen_type == MP_FROZEN_MPY) { - do_execute_raw_code(module_obj, modref); + do_execute_raw_code(module_obj, modref, file_str); return; } #endif @@ -216,7 +216,7 @@ STATIC void do_load(mp_obj_t module_obj, vstr_t *file) { #if MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD if (file_str[file->len - 3] == 'm') { mp_raw_code_t *raw_code = mp_raw_code_load_file(file_str); - do_execute_raw_code(module_obj, raw_code); + do_execute_raw_code(module_obj, raw_code, file_str); return; } #endif @@ -385,21 +385,18 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { DEBUG_printf("Current path: %.*s\n", vstr_len(&path), vstr_str(&path)); if (stat == MP_IMPORT_STAT_NO_EXIST) { + module_obj = MP_OBJ_NULL; #if MICROPY_MODULE_WEAK_LINKS // check if there is a weak link to this module if (i == mod_len) { - mp_map_elem_t *el = mp_map_lookup((mp_map_t*)&mp_builtin_module_weak_links_map, MP_OBJ_NEW_QSTR(mod_name), MP_MAP_LOOKUP); - if (el == NULL) { - goto no_exist; + module_obj = mp_module_search_umodule(mod_str); + if (module_obj != MP_OBJ_NULL) { + // found weak linked module + mp_module_call_init(mod_name, module_obj); } - // found weak linked module - module_obj = el->value; - mp_module_call_init(mod_name, module_obj); - } else { - no_exist: - #else - { + } #endif + if (module_obj == MP_OBJ_NULL) { // couldn't find the file, so fail if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { mp_raise_msg(&mp_type_ImportError, "module not found"); @@ -496,11 +493,11 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { #if MICROPY_MODULE_WEAK_LINKS // Check if there is a weak link to this module - mp_map_elem_t *el = mp_map_lookup((mp_map_t*)&mp_builtin_module_weak_links_map, MP_OBJ_NEW_QSTR(module_name_qstr), MP_MAP_LOOKUP); - if (el != NULL) { + module_obj = mp_module_search_umodule(qstr_str(module_name_qstr)); + if (module_obj != MP_OBJ_NULL) { // Found weak-linked module - mp_module_call_init(module_name_qstr, el->value); - return el->value; + mp_module_call_init(module_name_qstr, module_obj); + return module_obj; } #endif diff --git a/python/src/py/compile.c b/python/src/py/compile.c index 27b706c8f..0d36aef8b 100644 --- a/python/src/py/compile.c +++ b/python/src/py/compile.c @@ -95,6 +95,7 @@ STATIC const emit_method_table_t *emit_native_table[] = { &emit_native_thumb_method_table, &emit_native_thumb_method_table, &emit_native_xtensa_method_table, + &emit_native_xtensawin_method_table, }; #elif MICROPY_EMIT_NATIVE @@ -109,6 +110,8 @@ STATIC const emit_method_table_t *emit_native_table[] = { #define NATIVE_EMITTER(f) emit_native_arm_##f #elif MICROPY_EMIT_XTENSA #define NATIVE_EMITTER(f) emit_native_xtensa_##f +#elif MICROPY_EMIT_XTENSAWIN +#define NATIVE_EMITTER(f) emit_native_xtensawin_##f #else #error "unknown native emitter" #endif @@ -131,6 +134,7 @@ STATIC const emit_inline_asm_method_table_t *emit_asm_table[] = { &emit_inline_thumb_method_table, &emit_inline_thumb_method_table, &emit_inline_xtensa_method_table, + NULL, }; #elif MICROPY_EMIT_INLINE_ASM @@ -1024,16 +1028,13 @@ STATIC void compile_del_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { STATIC void compile_break_cont_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { uint16_t label; - const char *error_msg; if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_break_stmt) { label = comp->break_label; - error_msg = "'break' outside loop"; } else { label = comp->continue_label; - error_msg = "'continue' outside loop"; } if (label == INVALID_LABEL) { - compile_syntax_error(comp, (mp_parse_node_t)pns, error_msg); + compile_syntax_error(comp, (mp_parse_node_t)pns, "'break'/'continue' outside loop"); } assert(comp->cur_except_level >= comp->break_continue_except_level); EMIT_ARG(unwind_jump, label, comp->cur_except_level - comp->break_continue_except_level); @@ -1196,6 +1197,13 @@ STATIC void compile_import_from(compiler_t *comp, mp_parse_node_struct_t *pns) { } while (0); if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_STAR)) { + #if MICROPY_CPYTHON_COMPAT + if (comp->scope_cur->kind != SCOPE_MODULE) { + compile_syntax_error(comp, (mp_parse_node_t)pns, "import * not at module level"); + return; + } + #endif + EMIT_ARG(load_const_small_int, import_level); // build the "fromlist" tuple @@ -1205,7 +1213,7 @@ STATIC void compile_import_from(compiler_t *comp, mp_parse_node_struct_t *pns) { // do the import qstr dummy_q; do_import_name(comp, pn_import_source, &dummy_q); - EMIT_ARG(import, MP_QSTR_NULL, MP_EMIT_IMPORT_STAR); + EMIT_ARG(import, MP_QSTRnull, MP_EMIT_IMPORT_STAR); } else { EMIT_ARG(load_const_small_int, import_level); @@ -1608,6 +1616,9 @@ STATIC void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_ qstr qstr_exception_local = 0; uint end_finally_label = comp_next_label(comp); + #if MICROPY_PY_SYS_SETTRACE + EMIT_ARG(set_source_line, pns_except->source_line); + #endif if (MP_PARSE_NODE_IS_NULL(pns_except->nodes[0])) { // this is a catch all exception handler @@ -1993,21 +2004,8 @@ STATIC void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { c_assign(comp, pns->nodes[0], ASSIGN_AUG_LOAD); // lhs load for aug assign compile_node(comp, pns1->nodes[1]); // rhs assert(MP_PARSE_NODE_IS_TOKEN(pns1->nodes[0])); - mp_binary_op_t op; - switch (MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0])) { - case MP_TOKEN_DEL_PIPE_EQUAL: op = MP_BINARY_OP_INPLACE_OR; break; - case MP_TOKEN_DEL_CARET_EQUAL: op = MP_BINARY_OP_INPLACE_XOR; break; - case MP_TOKEN_DEL_AMPERSAND_EQUAL: op = MP_BINARY_OP_INPLACE_AND; break; - case MP_TOKEN_DEL_DBL_LESS_EQUAL: op = MP_BINARY_OP_INPLACE_LSHIFT; break; - case MP_TOKEN_DEL_DBL_MORE_EQUAL: op = MP_BINARY_OP_INPLACE_RSHIFT; break; - case MP_TOKEN_DEL_PLUS_EQUAL: op = MP_BINARY_OP_INPLACE_ADD; break; - case MP_TOKEN_DEL_MINUS_EQUAL: op = MP_BINARY_OP_INPLACE_SUBTRACT; break; - case MP_TOKEN_DEL_STAR_EQUAL: op = MP_BINARY_OP_INPLACE_MULTIPLY; break; - case MP_TOKEN_DEL_DBL_SLASH_EQUAL: op = MP_BINARY_OP_INPLACE_FLOOR_DIVIDE; break; - case MP_TOKEN_DEL_SLASH_EQUAL: op = MP_BINARY_OP_INPLACE_TRUE_DIVIDE; break; - case MP_TOKEN_DEL_PERCENT_EQUAL: op = MP_BINARY_OP_INPLACE_MODULO; break; - case MP_TOKEN_DEL_DBL_STAR_EQUAL: default: op = MP_BINARY_OP_INPLACE_POWER; break; - } + mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]); + mp_binary_op_t op = MP_BINARY_OP_INPLACE_OR + (tok - MP_TOKEN_DEL_PIPE_EQUAL); EMIT_ARG(binary_op, op); c_assign(comp, pns->nodes[0], ASSIGN_AUG_STORE); // lhs store for aug assign } else if (kind == PN_expr_stmt_assign_list) { @@ -2141,15 +2139,12 @@ STATIC void compile_comparison(compiler_t *comp, mp_parse_node_struct_t *pns) { EMIT(rot_three); } if (MP_PARSE_NODE_IS_TOKEN(pns->nodes[i])) { + mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]); mp_binary_op_t op; - switch (MP_PARSE_NODE_LEAF_ARG(pns->nodes[i])) { - case MP_TOKEN_OP_LESS: op = MP_BINARY_OP_LESS; break; - case MP_TOKEN_OP_MORE: op = MP_BINARY_OP_MORE; break; - case MP_TOKEN_OP_DBL_EQUAL: op = MP_BINARY_OP_EQUAL; break; - case MP_TOKEN_OP_LESS_EQUAL: op = MP_BINARY_OP_LESS_EQUAL; break; - case MP_TOKEN_OP_MORE_EQUAL: op = MP_BINARY_OP_MORE_EQUAL; break; - case MP_TOKEN_OP_NOT_EQUAL: op = MP_BINARY_OP_NOT_EQUAL; break; - case MP_TOKEN_KW_IN: default: op = MP_BINARY_OP_IN; break; + if (tok == MP_TOKEN_KW_IN) { + op = MP_BINARY_OP_IN; + } else { + op = MP_BINARY_OP_LESS + (tok - MP_TOKEN_OP_LESS); } EMIT_ARG(binary_op, op); } else { @@ -2203,36 +2198,21 @@ STATIC void compile_term(compiler_t *comp, mp_parse_node_struct_t *pns) { compile_node(comp, pns->nodes[0]); for (int i = 1; i + 1 < num_nodes; i += 2) { compile_node(comp, pns->nodes[i + 1]); - mp_binary_op_t op; mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]); - switch (tok) { - case MP_TOKEN_OP_PLUS: op = MP_BINARY_OP_ADD; break; - case MP_TOKEN_OP_MINUS: op = MP_BINARY_OP_SUBTRACT; break; - case MP_TOKEN_OP_STAR: op = MP_BINARY_OP_MULTIPLY; break; - case MP_TOKEN_OP_DBL_SLASH: op = MP_BINARY_OP_FLOOR_DIVIDE; break; - case MP_TOKEN_OP_SLASH: op = MP_BINARY_OP_TRUE_DIVIDE; break; - case MP_TOKEN_OP_PERCENT: op = MP_BINARY_OP_MODULO; break; - case MP_TOKEN_OP_DBL_LESS: op = MP_BINARY_OP_LSHIFT; break; - default: - assert(tok == MP_TOKEN_OP_DBL_MORE); - op = MP_BINARY_OP_RSHIFT; - break; - } + mp_binary_op_t op = MP_BINARY_OP_LSHIFT + (tok - MP_TOKEN_OP_DBL_LESS); EMIT_ARG(binary_op, op); } } STATIC void compile_factor_2(compiler_t *comp, mp_parse_node_struct_t *pns) { compile_node(comp, pns->nodes[1]); - mp_unary_op_t op; mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); - switch (tok) { - case MP_TOKEN_OP_PLUS: op = MP_UNARY_OP_POSITIVE; break; - case MP_TOKEN_OP_MINUS: op = MP_UNARY_OP_NEGATIVE; break; - default: - assert(tok == MP_TOKEN_OP_TILDE); - op = MP_UNARY_OP_INVERT; - break; + mp_unary_op_t op; + if (tok == MP_TOKEN_OP_TILDE) { + op = MP_UNARY_OP_INVERT; + } else { + assert(tok == MP_TOKEN_OP_PLUS || tok == MP_TOKEN_OP_MINUS); + op = MP_UNARY_OP_POSITIVE + (tok - MP_TOKEN_OP_PLUS); } EMIT_ARG(unary_op, op); } @@ -2851,7 +2831,7 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn return; } - qstr param_name = MP_QSTR_NULL; + qstr param_name = MP_QSTRnull; uint param_flag = ID_FLAG_IS_PARAM; mp_parse_node_struct_t *pns = NULL; if (MP_PARSE_NODE_IS_ID(pn)) { @@ -2910,7 +2890,7 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn } } - if (param_name != MP_QSTR_NULL) { + if (param_name != MP_QSTRnull) { id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, param_name, ID_INFO_KIND_UNDECIDED); if (id_info->kind != ID_INFO_KIND_UNDECIDED) { compile_syntax_error(comp, pn, "argument name reused"); @@ -3078,6 +3058,9 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn; assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 3); + // Set the source line number for the start of the lambda + EMIT_ARG(set_source_line, pns->source_line); + // work out number of parameters, keywords and default parameters, and add them to the id_info array // must be done before compiling the body so that arguments are numbered first (for LOAD_FAST etc) if (comp->pass == MP_PASS_SCOPE) { @@ -3112,6 +3095,9 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { scope->num_pos_args = 1; } + // Set the source line number for the start of the comprehension + EMIT_ARG(set_source_line, pns->source_line); + if (scope->kind == SCOPE_LIST_COMP) { EMIT_ARG(build, 0, MP_EMIT_BUILD_LIST); } else if (scope->kind == SCOPE_DICT_COMP) { @@ -3151,6 +3137,9 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { scope_find_or_add_id(scope, MP_QSTR___class__, ID_INFO_KIND_LOCAL); } + #if MICROPY_PY_SYS_SETTRACE + EMIT_ARG(set_source_line, pns->source_line); + #endif compile_load_id(comp, MP_QSTR___name__); compile_store_id(comp, MP_QSTR___module__); EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0])); // 0 is class name @@ -3434,7 +3423,7 @@ STATIC void scope_compute_things(scope_t *scope) { #if !MICROPY_PERSISTENT_CODE_SAVE STATIC #endif -mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt, bool is_repl) { +mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) { // put compiler state on the stack, it's relatively small compiler_t comp_state = {0}; compiler_t *comp = &comp_state; @@ -3445,6 +3434,11 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f comp->continue_label = INVALID_LABEL; // create the module scope + #if MICROPY_EMIT_NATIVE + const uint emit_opt = MP_STATE_VM(default_emit_opt); + #else + const uint emit_opt = MP_EMIT_OPT_NONE; + #endif scope_t *module_scope = scope_new_and_link(comp, SCOPE_MODULE, parse_tree->root, emit_opt); // create standard emitter; it's used at least for MP_PASS_SCOPE @@ -3599,8 +3593,8 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f } } -mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt, bool is_repl) { - mp_raw_code_t *rc = mp_compile_to_raw_code(parse_tree, source_file, emit_opt, is_repl); +mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) { + mp_raw_code_t *rc = mp_compile_to_raw_code(parse_tree, source_file, is_repl); // return function that executes the outer module return mp_make_function_from_raw_code(rc, MP_OBJ_NULL, MP_OBJ_NULL); } diff --git a/python/src/py/compile.h b/python/src/py/compile.h index 99a17a8d1..1ad1f5e9c 100644 --- a/python/src/py/compile.h +++ b/python/src/py/compile.h @@ -32,11 +32,11 @@ // the compiler will raise an exception if an error occurred // the compiler will clear the parse tree before it returns -mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt, bool is_repl); +mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl); #if MICROPY_PERSISTENT_CODE_SAVE // this has the same semantics as mp_compile -mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt, bool is_repl); +mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl); #endif // this is implemented in runtime.c diff --git a/python/src/py/emit.h b/python/src/py/emit.h index 70f7bf122..26d027a7a 100644 --- a/python/src/py/emit.h +++ b/python/src/py/emit.h @@ -76,15 +76,15 @@ typedef enum { // Kind for emit->setup_block() #define MP_EMIT_SETUP_BLOCK_WITH (0) -#define MP_EMIT_SETUP_BLOCK_EXCEPT (2) -#define MP_EMIT_SETUP_BLOCK_FINALLY (3) +#define MP_EMIT_SETUP_BLOCK_EXCEPT (1) +#define MP_EMIT_SETUP_BLOCK_FINALLY (2) // Kind for emit->build() #define MP_EMIT_BUILD_TUPLE (0) #define MP_EMIT_BUILD_LIST (1) -#define MP_EMIT_BUILD_MAP (3) -#define MP_EMIT_BUILD_SET (6) -#define MP_EMIT_BUILD_SLICE (8) +#define MP_EMIT_BUILD_MAP (2) +#define MP_EMIT_BUILD_SET (3) +#define MP_EMIT_BUILD_SLICE (4) // Kind for emit->yield() #define MP_EMIT_YIELD_VALUE (0) @@ -174,6 +174,7 @@ extern const emit_method_table_t emit_native_x86_method_table; extern const emit_method_table_t emit_native_thumb_method_table; extern const emit_method_table_t emit_native_arm_method_table; extern const emit_method_table_t emit_native_xtensa_method_table; +extern const emit_method_table_t emit_native_xtensawin_method_table; extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_load_id_ops; extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_store_id_ops; @@ -185,6 +186,7 @@ emit_t *emit_native_x86_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t ma emit_t *emit_native_thumb_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); emit_t *emit_native_arm_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); emit_t *emit_native_xtensa_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_xtensawin_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); void emit_bc_set_max_num_labels(emit_t* emit, mp_uint_t max_num_labels); @@ -194,6 +196,7 @@ void emit_native_x86_free(emit_t *emit); void emit_native_thumb_free(emit_t *emit); void emit_native_arm_free(emit_t *emit); void emit_native_xtensa_free(emit_t *emit); +void emit_native_xtensawin_free(emit_t *emit); void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope); void mp_emit_bc_end_pass(emit_t *emit); diff --git a/python/src/py/emitbc.c b/python/src/py/emitbc.c index 35eb6df9c..34f6362ff 100644 --- a/python/src/py/emitbc.c +++ b/python/src/py/emitbc.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013-2019 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -64,6 +64,9 @@ struct _emit_t { size_t bytecode_size; byte *code_base; // stores both byte code and code info + size_t n_info; + size_t n_cell; + #if MICROPY_PERSISTENT_CODE uint16_t ct_cur_obj; uint16_t ct_num_obj; @@ -123,10 +126,6 @@ STATIC void emit_write_code_info_byte(emit_t* emit, byte val) { *emit_get_cur_to_write_code_info(emit, 1) = val; } -STATIC void emit_write_code_info_uint(emit_t* emit, mp_uint_t val) { - emit_write_uint(emit, emit_get_cur_to_write_code_info, val); -} - STATIC void emit_write_code_info_qstr(emit_t *emit, qstr qst) { #if MICROPY_PERSISTENT_CODE assert((qst >> 16) == 0); @@ -182,20 +181,20 @@ STATIC byte *emit_get_cur_to_write_bytecode(emit_t *emit, int num_bytes_to_write } } -STATIC void emit_write_bytecode_byte(emit_t *emit, byte b1) { +STATIC void emit_write_bytecode_raw_byte(emit_t *emit, byte b1) { byte *c = emit_get_cur_to_write_bytecode(emit, 1); c[0] = b1; } -STATIC void emit_write_bytecode_byte_byte(emit_t* emit, byte b1, byte b2) { - byte *c = emit_get_cur_to_write_bytecode(emit, 2); +STATIC void emit_write_bytecode_byte(emit_t *emit, int stack_adj, byte b1) { + mp_emit_bc_adjust_stack_size(emit, stack_adj); + byte *c = emit_get_cur_to_write_bytecode(emit, 1); c[0] = b1; - c[1] = b2; } // Similar to emit_write_bytecode_uint(), just some extra handling to encode sign -STATIC void emit_write_bytecode_byte_int(emit_t *emit, byte b1, mp_int_t num) { - emit_write_bytecode_byte(emit, b1); +STATIC void emit_write_bytecode_byte_int(emit_t *emit, int stack_adj, byte b1, mp_int_t num) { + emit_write_bytecode_byte(emit, stack_adj, b1); // We store each 7 bits in a separate byte, and that's how many bytes needed byte buf[BYTES_FOR_INT]; @@ -220,40 +219,41 @@ STATIC void emit_write_bytecode_byte_int(emit_t *emit, byte b1, mp_int_t num) { *c = *p; } -STATIC void emit_write_bytecode_byte_uint(emit_t *emit, byte b, mp_uint_t val) { - emit_write_bytecode_byte(emit, b); +STATIC void emit_write_bytecode_byte_uint(emit_t *emit, int stack_adj, byte b, mp_uint_t val) { + emit_write_bytecode_byte(emit, stack_adj, b); emit_write_uint(emit, emit_get_cur_to_write_bytecode, val); } #if MICROPY_PERSISTENT_CODE -STATIC void emit_write_bytecode_byte_const(emit_t *emit, byte b, mp_uint_t n, mp_uint_t c) { +STATIC void emit_write_bytecode_byte_const(emit_t *emit, int stack_adj, byte b, mp_uint_t n, mp_uint_t c) { if (emit->pass == MP_PASS_EMIT) { emit->const_table[n] = c; } - emit_write_bytecode_byte_uint(emit, b, n); + emit_write_bytecode_byte_uint(emit, stack_adj, b, n); } #endif -STATIC void emit_write_bytecode_byte_qstr(emit_t* emit, byte b, qstr qst) { +STATIC void emit_write_bytecode_byte_qstr(emit_t* emit, int stack_adj, byte b, qstr qst) { #if MICROPY_PERSISTENT_CODE assert((qst >> 16) == 0); + mp_emit_bc_adjust_stack_size(emit, stack_adj); byte *c = emit_get_cur_to_write_bytecode(emit, 3); c[0] = b; c[1] = qst; c[2] = qst >> 8; #else - emit_write_bytecode_byte_uint(emit, b, qst); + emit_write_bytecode_byte_uint(emit, stack_adj, b, qst); #endif } -STATIC void emit_write_bytecode_byte_obj(emit_t *emit, byte b, mp_obj_t obj) { +STATIC void emit_write_bytecode_byte_obj(emit_t *emit, int stack_adj, byte b, mp_obj_t obj) { #if MICROPY_PERSISTENT_CODE - emit_write_bytecode_byte_const(emit, b, + emit_write_bytecode_byte_const(emit, stack_adj, b, emit->scope->num_pos_args + emit->scope->num_kwonly_args + emit->ct_cur_obj++, (mp_uint_t)obj); #else // aligns the pointer so it is friendly to GC - emit_write_bytecode_byte(emit, b); + emit_write_bytecode_byte(emit, stack_adj, b); emit->bytecode_offset = (size_t)MP_ALIGN(emit->bytecode_offset, sizeof(mp_obj_t)); mp_obj_t *c = (mp_obj_t*)emit_get_cur_to_write_bytecode(emit, sizeof(mp_obj_t)); // Verify thar c is already uint-aligned @@ -262,24 +262,28 @@ STATIC void emit_write_bytecode_byte_obj(emit_t *emit, byte b, mp_obj_t obj) { #endif } -STATIC void emit_write_bytecode_byte_raw_code(emit_t *emit, byte b, mp_raw_code_t *rc) { +STATIC void emit_write_bytecode_byte_raw_code(emit_t *emit, int stack_adj, byte b, mp_raw_code_t *rc) { #if MICROPY_PERSISTENT_CODE - emit_write_bytecode_byte_const(emit, b, + emit_write_bytecode_byte_const(emit, stack_adj, b, emit->scope->num_pos_args + emit->scope->num_kwonly_args + emit->ct_num_obj + emit->ct_cur_raw_code++, (mp_uint_t)(uintptr_t)rc); #else // aligns the pointer so it is friendly to GC - emit_write_bytecode_byte(emit, b); + emit_write_bytecode_byte(emit, stack_adj, b); emit->bytecode_offset = (size_t)MP_ALIGN(emit->bytecode_offset, sizeof(void*)); void **c = (void**)emit_get_cur_to_write_bytecode(emit, sizeof(void*)); // Verify thar c is already uint-aligned assert(c == MP_ALIGN(c, sizeof(void*))); *c = rc; #endif + #if MICROPY_PY_SYS_SETTRACE + rc->line_of_definition = emit->last_source_line; + #endif } // unsigned labels are relative to ip following this instruction, stored as 16 bits -STATIC void emit_write_bytecode_byte_unsigned_label(emit_t *emit, byte b1, mp_uint_t label) { +STATIC void emit_write_bytecode_byte_unsigned_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) { + mp_emit_bc_adjust_stack_size(emit, stack_adj); mp_uint_t bytecode_offset; if (emit->pass < MP_PASS_EMIT) { bytecode_offset = 0; @@ -293,7 +297,8 @@ STATIC void emit_write_bytecode_byte_unsigned_label(emit_t *emit, byte b1, mp_ui } // signed labels are relative to ip following this instruction, stored as 16 bits, in excess -STATIC void emit_write_bytecode_byte_signed_label(emit_t *emit, byte b1, mp_uint_t label) { +STATIC void emit_write_bytecode_byte_signed_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) { + mp_emit_bc_adjust_stack_size(emit, stack_adj); int bytecode_offset; if (emit->pass < MP_PASS_EMIT) { bytecode_offset = 0; @@ -322,7 +327,7 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { emit->bytecode_offset = 0; emit->code_info_offset = 0; - // Write local state size and exception stack size. + // Write local state size, exception stack size, scope flags and number of arguments { mp_uint_t n_state = scope->num_locals + scope->stack_size; if (n_state == 0) { @@ -335,40 +340,22 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { // An extra slot in the stack is needed to detect VM stack overflow n_state += 1; #endif - emit_write_code_info_uint(emit, n_state); - emit_write_code_info_uint(emit, scope->exc_stack_size); + + size_t n_exc_stack = scope->exc_stack_size; + MP_BC_PRELUDE_SIG_ENCODE(n_state, n_exc_stack, scope, emit_write_code_info_byte, emit); } - // Write scope flags and number of arguments. - // TODO check that num args all fit in a byte - emit_write_code_info_byte(emit, emit->scope->scope_flags); - emit_write_code_info_byte(emit, emit->scope->num_pos_args); - emit_write_code_info_byte(emit, emit->scope->num_kwonly_args); - emit_write_code_info_byte(emit, emit->scope->num_def_pos_args); - - // Write size of the rest of the code info. We don't know how big this - // variable uint will be on the MP_PASS_CODE_SIZE pass so we reserve 2 bytes - // for it and hope that is enough! TODO assert this or something. - if (pass == MP_PASS_EMIT) { - emit_write_code_info_uint(emit, emit->code_info_size - emit->code_info_offset); - } else { - emit_get_cur_to_write_code_info(emit, 2); + // Write number of cells and size of the source code info + if (pass >= MP_PASS_CODE_SIZE) { + MP_BC_PRELUDE_SIZE_ENCODE(emit->n_info, emit->n_cell, emit_write_code_info_byte, emit); } + emit->n_info = emit->code_info_offset; + // Write the name and source file of this function. emit_write_code_info_qstr(emit, scope->simple_name); emit_write_code_info_qstr(emit, scope->source_file); - // bytecode prelude: initialise closed over variables - for (int i = 0; i < scope->id_info_len; i++) { - id_info_t *id = &scope->id_info[i]; - if (id->kind == ID_INFO_KIND_CELL) { - assert(id->local_num < 255); - emit_write_bytecode_byte(emit, id->local_num); // write the local which should be converted to a cell - } - } - emit_write_bytecode_byte(emit, 255); // end of list sentinel - #if MICROPY_PERSISTENT_CODE emit->ct_cur_obj = 0; emit->ct_cur_raw_code = 0; @@ -414,6 +401,20 @@ void mp_emit_bc_end_pass(emit_t *emit) { emit_write_code_info_byte(emit, 0); // end of line number info + // Calculate size of source code info section + emit->n_info = emit->code_info_offset - emit->n_info; + + // Emit closure section of prelude + emit->n_cell = 0; + for (size_t i = 0; i < emit->scope->id_info_len; ++i) { + id_info_t *id = &emit->scope->id_info[i]; + if (id->kind == ID_INFO_KIND_CELL) { + assert(id->local_num <= 255); + emit_write_code_info_byte(emit, id->local_num); // write the local which should be converted to a cell + ++emit->n_cell; + } + } + #if MICROPY_PERSISTENT_CODE assert(emit->pass <= MP_PASS_STACK_SIZE || (emit->ct_num_obj == emit->ct_cur_obj)); emit->ct_num_obj = emit->ct_cur_obj; @@ -468,10 +469,6 @@ void mp_emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta) { emit->last_emit_was_return_value = false; } -static inline void emit_bc_pre(emit_t *emit, mp_int_t stack_size_delta) { - mp_emit_bc_adjust_stack_size(emit, stack_size_delta); -} - void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t source_line) { //printf("source: line %d -> %d offset %d -> %d\n", emit->last_source_line, source_line, emit->last_source_line_offset, emit->bytecode_offset); #if MICROPY_ENABLE_SOURCE_LINE @@ -493,7 +490,7 @@ void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t source_line) { } void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l) { - emit_bc_pre(emit, 0); + mp_emit_bc_adjust_stack_size(emit, 0); if (emit->pass == MP_PASS_SCOPE) { return; } @@ -511,64 +508,54 @@ void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l) { void mp_emit_bc_import(emit_t *emit, qstr qst, int kind) { MP_STATIC_ASSERT(MP_BC_IMPORT_NAME + MP_EMIT_IMPORT_NAME == MP_BC_IMPORT_NAME); MP_STATIC_ASSERT(MP_BC_IMPORT_NAME + MP_EMIT_IMPORT_FROM == MP_BC_IMPORT_FROM); - if (kind == MP_EMIT_IMPORT_FROM) { - emit_bc_pre(emit, 1); - } else { - emit_bc_pre(emit, -1); - } + int stack_adj = kind == MP_EMIT_IMPORT_FROM ? 1 : -1; if (kind == MP_EMIT_IMPORT_STAR) { - emit_write_bytecode_byte(emit, MP_BC_IMPORT_STAR); + emit_write_bytecode_byte(emit, stack_adj, MP_BC_IMPORT_STAR); } else { - emit_write_bytecode_byte_qstr(emit, MP_BC_IMPORT_NAME + kind, qst); + emit_write_bytecode_byte_qstr(emit, stack_adj, MP_BC_IMPORT_NAME + kind, qst); } } void mp_emit_bc_load_const_tok(emit_t *emit, mp_token_kind_t tok) { - emit_bc_pre(emit, 1); - switch (tok) { - case MP_TOKEN_KW_FALSE: emit_write_bytecode_byte(emit, MP_BC_LOAD_CONST_FALSE); break; - case MP_TOKEN_KW_NONE: emit_write_bytecode_byte(emit, MP_BC_LOAD_CONST_NONE); break; - case MP_TOKEN_KW_TRUE: emit_write_bytecode_byte(emit, MP_BC_LOAD_CONST_TRUE); break; - default: - assert(tok == MP_TOKEN_ELLIPSIS); - emit_write_bytecode_byte_obj(emit, MP_BC_LOAD_CONST_OBJ, MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj)); - break; + MP_STATIC_ASSERT(MP_BC_LOAD_CONST_FALSE + (MP_TOKEN_KW_NONE - MP_TOKEN_KW_FALSE) == MP_BC_LOAD_CONST_NONE); + MP_STATIC_ASSERT(MP_BC_LOAD_CONST_FALSE + (MP_TOKEN_KW_TRUE - MP_TOKEN_KW_FALSE) == MP_BC_LOAD_CONST_TRUE); + if (tok == MP_TOKEN_ELLIPSIS) { + emit_write_bytecode_byte_obj(emit, 1, MP_BC_LOAD_CONST_OBJ, MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj)); + } else { + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_CONST_FALSE + (tok - MP_TOKEN_KW_FALSE)); } } void mp_emit_bc_load_const_small_int(emit_t *emit, mp_int_t arg) { - emit_bc_pre(emit, 1); - if (-16 <= arg && arg <= 47) { - emit_write_bytecode_byte(emit, MP_BC_LOAD_CONST_SMALL_INT_MULTI + 16 + arg); + if (-MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS <= arg + && arg < MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM - MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS) { + emit_write_bytecode_byte(emit, 1, + MP_BC_LOAD_CONST_SMALL_INT_MULTI + MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS + arg); } else { - emit_write_bytecode_byte_int(emit, MP_BC_LOAD_CONST_SMALL_INT, arg); + emit_write_bytecode_byte_int(emit, 1, MP_BC_LOAD_CONST_SMALL_INT, arg); } } void mp_emit_bc_load_const_str(emit_t *emit, qstr qst) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_CONST_STRING, qst); + emit_write_bytecode_byte_qstr(emit, 1, MP_BC_LOAD_CONST_STRING, qst); } void mp_emit_bc_load_const_obj(emit_t *emit, mp_obj_t obj) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte_obj(emit, MP_BC_LOAD_CONST_OBJ, obj); + emit_write_bytecode_byte_obj(emit, 1, MP_BC_LOAD_CONST_OBJ, obj); } void mp_emit_bc_load_null(emit_t *emit) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte(emit, MP_BC_LOAD_NULL); + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_NULL); } void mp_emit_bc_load_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) { MP_STATIC_ASSERT(MP_BC_LOAD_FAST_N + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_LOAD_FAST_N); MP_STATIC_ASSERT(MP_BC_LOAD_FAST_N + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_LOAD_DEREF); (void)qst; - emit_bc_pre(emit, 1); if (kind == MP_EMIT_IDOP_LOCAL_FAST && local_num <= 15) { - emit_write_bytecode_byte(emit, MP_BC_LOAD_FAST_MULTI + local_num); + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_FAST_MULTI + local_num); } else { - emit_write_bytecode_byte_uint(emit, MP_BC_LOAD_FAST_N + kind, local_num); + emit_write_bytecode_byte_uint(emit, 1, MP_BC_LOAD_FAST_N + kind, local_num); } } @@ -576,51 +563,45 @@ void mp_emit_bc_load_global(emit_t *emit, qstr qst, int kind) { MP_STATIC_ASSERT(MP_BC_LOAD_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_LOAD_NAME); MP_STATIC_ASSERT(MP_BC_LOAD_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_LOAD_GLOBAL); (void)qst; - emit_bc_pre(emit, 1); - emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_NAME + kind, qst); + emit_write_bytecode_byte_qstr(emit, 1, MP_BC_LOAD_NAME + kind, qst); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) { - emit_write_bytecode_byte(emit, 0); + emit_write_bytecode_raw_byte(emit, 0); } } void mp_emit_bc_load_method(emit_t *emit, qstr qst, bool is_super) { - emit_bc_pre(emit, 1 - 2 * is_super); - emit_write_bytecode_byte_qstr(emit, is_super ? MP_BC_LOAD_SUPER_METHOD : MP_BC_LOAD_METHOD, qst); + int stack_adj = 1 - 2 * is_super; + emit_write_bytecode_byte_qstr(emit, stack_adj, is_super ? MP_BC_LOAD_SUPER_METHOD : MP_BC_LOAD_METHOD, qst); } void mp_emit_bc_load_build_class(emit_t *emit) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte(emit, MP_BC_LOAD_BUILD_CLASS); + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_BUILD_CLASS); } void mp_emit_bc_subscr(emit_t *emit, int kind) { if (kind == MP_EMIT_SUBSCR_LOAD) { - emit_bc_pre(emit, -1); - emit_write_bytecode_byte(emit, MP_BC_LOAD_SUBSCR); + emit_write_bytecode_byte(emit, -1, MP_BC_LOAD_SUBSCR); } else { if (kind == MP_EMIT_SUBSCR_DELETE) { mp_emit_bc_load_null(emit); mp_emit_bc_rot_three(emit); } - emit_bc_pre(emit, -3); - emit_write_bytecode_byte(emit, MP_BC_STORE_SUBSCR); + emit_write_bytecode_byte(emit, -3, MP_BC_STORE_SUBSCR); } } void mp_emit_bc_attr(emit_t *emit, qstr qst, int kind) { if (kind == MP_EMIT_ATTR_LOAD) { - emit_bc_pre(emit, 0); - emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_ATTR, qst); + emit_write_bytecode_byte_qstr(emit, 0, MP_BC_LOAD_ATTR, qst); } else { if (kind == MP_EMIT_ATTR_DELETE) { mp_emit_bc_load_null(emit); mp_emit_bc_rot_two(emit); } - emit_bc_pre(emit, -2); - emit_write_bytecode_byte_qstr(emit, MP_BC_STORE_ATTR, qst); + emit_write_bytecode_byte_qstr(emit, -2, MP_BC_STORE_ATTR, qst); } if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) { - emit_write_bytecode_byte(emit, 0); + emit_write_bytecode_raw_byte(emit, 0); } } @@ -628,98 +609,86 @@ void mp_emit_bc_store_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kin MP_STATIC_ASSERT(MP_BC_STORE_FAST_N + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_STORE_FAST_N); MP_STATIC_ASSERT(MP_BC_STORE_FAST_N + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_STORE_DEREF); (void)qst; - emit_bc_pre(emit, -1); if (kind == MP_EMIT_IDOP_LOCAL_FAST && local_num <= 15) { - emit_write_bytecode_byte(emit, MP_BC_STORE_FAST_MULTI + local_num); + emit_write_bytecode_byte(emit, -1, MP_BC_STORE_FAST_MULTI + local_num); } else { - emit_write_bytecode_byte_uint(emit, MP_BC_STORE_FAST_N + kind, local_num); + emit_write_bytecode_byte_uint(emit, -1, MP_BC_STORE_FAST_N + kind, local_num); } } void mp_emit_bc_store_global(emit_t *emit, qstr qst, int kind) { MP_STATIC_ASSERT(MP_BC_STORE_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_STORE_NAME); MP_STATIC_ASSERT(MP_BC_STORE_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_STORE_GLOBAL); - emit_bc_pre(emit, -1); - emit_write_bytecode_byte_qstr(emit, MP_BC_STORE_NAME + kind, qst); + emit_write_bytecode_byte_qstr(emit, -1, MP_BC_STORE_NAME + kind, qst); } void mp_emit_bc_delete_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) { MP_STATIC_ASSERT(MP_BC_DELETE_FAST + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_DELETE_FAST); MP_STATIC_ASSERT(MP_BC_DELETE_FAST + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_DELETE_DEREF); (void)qst; - emit_write_bytecode_byte_uint(emit, MP_BC_DELETE_FAST + kind, local_num); + emit_write_bytecode_byte_uint(emit, 0, MP_BC_DELETE_FAST + kind, local_num); } void mp_emit_bc_delete_global(emit_t *emit, qstr qst, int kind) { MP_STATIC_ASSERT(MP_BC_DELETE_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_DELETE_NAME); MP_STATIC_ASSERT(MP_BC_DELETE_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_DELETE_GLOBAL); - emit_bc_pre(emit, 0); - emit_write_bytecode_byte_qstr(emit, MP_BC_DELETE_NAME + kind, qst); + emit_write_bytecode_byte_qstr(emit, 0, MP_BC_DELETE_NAME + kind, qst); } void mp_emit_bc_dup_top(emit_t *emit) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte(emit, MP_BC_DUP_TOP); + emit_write_bytecode_byte(emit, 1, MP_BC_DUP_TOP); } void mp_emit_bc_dup_top_two(emit_t *emit) { - emit_bc_pre(emit, 2); - emit_write_bytecode_byte(emit, MP_BC_DUP_TOP_TWO); + emit_write_bytecode_byte(emit, 2, MP_BC_DUP_TOP_TWO); } void mp_emit_bc_pop_top(emit_t *emit) { - emit_bc_pre(emit, -1); - emit_write_bytecode_byte(emit, MP_BC_POP_TOP); + emit_write_bytecode_byte(emit, -1, MP_BC_POP_TOP); } void mp_emit_bc_rot_two(emit_t *emit) { - emit_bc_pre(emit, 0); - emit_write_bytecode_byte(emit, MP_BC_ROT_TWO); + emit_write_bytecode_byte(emit, 0, MP_BC_ROT_TWO); } void mp_emit_bc_rot_three(emit_t *emit) { - emit_bc_pre(emit, 0); - emit_write_bytecode_byte(emit, MP_BC_ROT_THREE); + emit_write_bytecode_byte(emit, 0, MP_BC_ROT_THREE); } void mp_emit_bc_jump(emit_t *emit, mp_uint_t label) { - emit_bc_pre(emit, 0); - emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP, label); + emit_write_bytecode_byte_signed_label(emit, 0, MP_BC_JUMP, label); } void mp_emit_bc_pop_jump_if(emit_t *emit, bool cond, mp_uint_t label) { - emit_bc_pre(emit, -1); if (cond) { - emit_write_bytecode_byte_signed_label(emit, MP_BC_POP_JUMP_IF_TRUE, label); + emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_POP_JUMP_IF_TRUE, label); } else { - emit_write_bytecode_byte_signed_label(emit, MP_BC_POP_JUMP_IF_FALSE, label); + emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_POP_JUMP_IF_FALSE, label); } } void mp_emit_bc_jump_if_or_pop(emit_t *emit, bool cond, mp_uint_t label) { - emit_bc_pre(emit, -1); if (cond) { - emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP_IF_TRUE_OR_POP, label); + emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_JUMP_IF_TRUE_OR_POP, label); } else { - emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP_IF_FALSE_OR_POP, label); + emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_JUMP_IF_FALSE_OR_POP, label); } } void mp_emit_bc_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_depth) { if (except_depth == 0) { - emit_bc_pre(emit, 0); if (label & MP_EMIT_BREAK_FROM_FOR) { // need to pop the iterator if we are breaking out of a for loop - emit_write_bytecode_byte(emit, MP_BC_POP_TOP); + emit_write_bytecode_raw_byte(emit, MP_BC_POP_TOP); // also pop the iter_buf for (size_t i = 0; i < MP_OBJ_ITER_BUF_NSLOTS - 1; ++i) { - emit_write_bytecode_byte(emit, MP_BC_POP_TOP); + emit_write_bytecode_raw_byte(emit, MP_BC_POP_TOP); } } - emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); + emit_write_bytecode_byte_signed_label(emit, 0, MP_BC_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); } else { - emit_write_bytecode_byte_signed_label(emit, MP_BC_UNWIND_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); - emit_write_bytecode_byte(emit, ((label & MP_EMIT_BREAK_FROM_FOR) ? 0x80 : 0) | except_depth); + emit_write_bytecode_byte_signed_label(emit, 0, MP_BC_UNWIND_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); + emit_write_bytecode_raw_byte(emit, ((label & MP_EMIT_BREAK_FROM_FOR) ? 0x80 : 0) | except_depth); } } @@ -727,52 +696,45 @@ void mp_emit_bc_setup_block(emit_t *emit, mp_uint_t label, int kind) { MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_WITH == MP_BC_SETUP_WITH); MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_EXCEPT == MP_BC_SETUP_EXCEPT); MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_FINALLY == MP_BC_SETUP_FINALLY); - if (kind == MP_EMIT_SETUP_BLOCK_WITH) { // The SETUP_WITH opcode pops ctx_mgr from the top of the stack // and then pushes 3 entries: __exit__, ctx_mgr, as_value. - emit_bc_pre(emit, 2); - } else { - emit_bc_pre(emit, 0); - } - emit_write_bytecode_byte_unsigned_label(emit, MP_BC_SETUP_WITH + kind, label); + int stack_adj = kind == MP_EMIT_SETUP_BLOCK_WITH ? 2 : 0; + emit_write_bytecode_byte_unsigned_label(emit, stack_adj, MP_BC_SETUP_WITH + kind, label); } void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label) { mp_emit_bc_load_const_tok(emit, MP_TOKEN_KW_NONE); mp_emit_bc_label_assign(emit, label); - emit_bc_pre(emit, 2); // ensure we have enough stack space to call the __exit__ method - emit_write_bytecode_byte(emit, MP_BC_WITH_CLEANUP); - emit_bc_pre(emit, -4); // cancel the 2 above, plus the 2 from mp_emit_bc_setup_block(MP_EMIT_SETUP_BLOCK_WITH) + // The +2 is to ensure we have enough stack space to call the __exit__ method + emit_write_bytecode_byte(emit, 2, MP_BC_WITH_CLEANUP); + // Cancel the +2 above, plus the +2 from mp_emit_bc_setup_block(MP_EMIT_SETUP_BLOCK_WITH) + mp_emit_bc_adjust_stack_size(emit, -4); } void mp_emit_bc_end_finally(emit_t *emit) { - emit_bc_pre(emit, -1); - emit_write_bytecode_byte(emit, MP_BC_END_FINALLY); + emit_write_bytecode_byte(emit, -1, MP_BC_END_FINALLY); } void mp_emit_bc_get_iter(emit_t *emit, bool use_stack) { - emit_bc_pre(emit, use_stack ? MP_OBJ_ITER_BUF_NSLOTS - 1 : 0); - emit_write_bytecode_byte(emit, use_stack ? MP_BC_GET_ITER_STACK : MP_BC_GET_ITER); + int stack_adj = use_stack ? MP_OBJ_ITER_BUF_NSLOTS - 1 : 0; + emit_write_bytecode_byte(emit, stack_adj, use_stack ? MP_BC_GET_ITER_STACK : MP_BC_GET_ITER); } void mp_emit_bc_for_iter(emit_t *emit, mp_uint_t label) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte_unsigned_label(emit, MP_BC_FOR_ITER, label); + emit_write_bytecode_byte_unsigned_label(emit, 1, MP_BC_FOR_ITER, label); } void mp_emit_bc_for_iter_end(emit_t *emit) { - emit_bc_pre(emit, -MP_OBJ_ITER_BUF_NSLOTS); + mp_emit_bc_adjust_stack_size(emit, -MP_OBJ_ITER_BUF_NSLOTS); } void mp_emit_bc_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler) { (void)within_exc_handler; - emit_bc_pre(emit, 0); - emit_write_bytecode_byte_unsigned_label(emit, MP_BC_POP_EXCEPT_JUMP, label); + emit_write_bytecode_byte_unsigned_label(emit, 0, MP_BC_POP_EXCEPT_JUMP, label); } void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op) { - emit_bc_pre(emit, 0); - emit_write_bytecode_byte(emit, MP_BC_UNARY_OP_MULTI + op); + emit_write_bytecode_byte(emit, 0, MP_BC_UNARY_OP_MULTI + op); } void mp_emit_bc_binary_op(emit_t *emit, mp_binary_op_t op) { @@ -784,11 +746,9 @@ void mp_emit_bc_binary_op(emit_t *emit, mp_binary_op_t op) { invert = true; op = MP_BINARY_OP_IS; } - emit_bc_pre(emit, -1); - emit_write_bytecode_byte(emit, MP_BC_BINARY_OP_MULTI + op); + emit_write_bytecode_byte(emit, -1, MP_BC_BINARY_OP_MULTI + op); if (invert) { - emit_bc_pre(emit, 0); - emit_write_bytecode_byte(emit, MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NOT); + emit_write_bytecode_byte(emit, 0, MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NOT); } } @@ -798,17 +758,12 @@ void mp_emit_bc_build(emit_t *emit, mp_uint_t n_args, int kind) { MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_MAP == MP_BC_BUILD_MAP); MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_SET == MP_BC_BUILD_SET); MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_SLICE == MP_BC_BUILD_SLICE); - if (kind == MP_EMIT_BUILD_MAP) { - emit_bc_pre(emit, 1); - } else { - emit_bc_pre(emit, 1 - n_args); - } - emit_write_bytecode_byte_uint(emit, MP_BC_BUILD_TUPLE + kind, n_args); + int stack_adj = kind == MP_EMIT_BUILD_MAP ? 1 : 1 - n_args; + emit_write_bytecode_byte_uint(emit, stack_adj, MP_BC_BUILD_TUPLE + kind, n_args); } void mp_emit_bc_store_map(emit_t *emit) { - emit_bc_pre(emit, -2); - emit_write_bytecode_byte(emit, MP_BC_STORE_MAP); + emit_write_bytecode_byte(emit, -2, MP_BC_STORE_MAP); } void mp_emit_bc_store_comp(emit_t *emit, scope_kind_t kind, mp_uint_t collection_stack_index) { @@ -824,51 +779,46 @@ void mp_emit_bc_store_comp(emit_t *emit, scope_kind_t kind, mp_uint_t collection n = 0; t = 2; } - emit_bc_pre(emit, -1 - n); // the lower 2 bits of the opcode argument indicate the collection type - emit_write_bytecode_byte_uint(emit, MP_BC_STORE_COMP, ((collection_stack_index + n) << 2) | t); + emit_write_bytecode_byte_uint(emit, -1 - n, MP_BC_STORE_COMP, ((collection_stack_index + n) << 2) | t); } void mp_emit_bc_unpack_sequence(emit_t *emit, mp_uint_t n_args) { - emit_bc_pre(emit, -1 + n_args); - emit_write_bytecode_byte_uint(emit, MP_BC_UNPACK_SEQUENCE, n_args); + emit_write_bytecode_byte_uint(emit, -1 + n_args, MP_BC_UNPACK_SEQUENCE, n_args); } void mp_emit_bc_unpack_ex(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right) { - emit_bc_pre(emit, -1 + n_left + n_right + 1); - emit_write_bytecode_byte_uint(emit, MP_BC_UNPACK_EX, n_left | (n_right << 8)); + emit_write_bytecode_byte_uint(emit, -1 + n_left + n_right + 1, MP_BC_UNPACK_EX, n_left | (n_right << 8)); } void mp_emit_bc_make_function(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) { if (n_pos_defaults == 0 && n_kw_defaults == 0) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte_raw_code(emit, MP_BC_MAKE_FUNCTION, scope->raw_code); + emit_write_bytecode_byte_raw_code(emit, 1, MP_BC_MAKE_FUNCTION, scope->raw_code); } else { - emit_bc_pre(emit, -1); - emit_write_bytecode_byte_raw_code(emit, MP_BC_MAKE_FUNCTION_DEFARGS, scope->raw_code); + emit_write_bytecode_byte_raw_code(emit, -1, MP_BC_MAKE_FUNCTION_DEFARGS, scope->raw_code); } } void mp_emit_bc_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) { if (n_pos_defaults == 0 && n_kw_defaults == 0) { - emit_bc_pre(emit, -n_closed_over + 1); - emit_write_bytecode_byte_raw_code(emit, MP_BC_MAKE_CLOSURE, scope->raw_code); - emit_write_bytecode_byte(emit, n_closed_over); + int stack_adj = -n_closed_over + 1; + emit_write_bytecode_byte_raw_code(emit, stack_adj, MP_BC_MAKE_CLOSURE, scope->raw_code); + emit_write_bytecode_raw_byte(emit, n_closed_over); } else { assert(n_closed_over <= 255); - emit_bc_pre(emit, -2 - (mp_int_t)n_closed_over + 1); - emit_write_bytecode_byte_raw_code(emit, MP_BC_MAKE_CLOSURE_DEFARGS, scope->raw_code); - emit_write_bytecode_byte(emit, n_closed_over); + int stack_adj = -2 - (mp_int_t)n_closed_over + 1; + emit_write_bytecode_byte_raw_code(emit, stack_adj, MP_BC_MAKE_CLOSURE_DEFARGS, scope->raw_code); + emit_write_bytecode_raw_byte(emit, n_closed_over); } } -STATIC void emit_bc_call_function_method_helper(emit_t *emit, mp_int_t stack_adj, mp_uint_t bytecode_base, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { +STATIC void emit_bc_call_function_method_helper(emit_t *emit, int stack_adj, mp_uint_t bytecode_base, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { if (star_flags) { - emit_bc_pre(emit, stack_adj - (mp_int_t)n_positional - 2 * (mp_int_t)n_keyword - 2); - emit_write_bytecode_byte_uint(emit, bytecode_base + 1, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? + stack_adj -= (int)n_positional + 2 * (int)n_keyword + 2; + emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base + 1, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? } else { - emit_bc_pre(emit, stack_adj - (mp_int_t)n_positional - 2 * (mp_int_t)n_keyword); - emit_write_bytecode_byte_uint(emit, bytecode_base, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? + stack_adj -= (int)n_positional + 2 * (int)n_keyword; + emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? } } @@ -881,22 +831,21 @@ void mp_emit_bc_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_ke } void mp_emit_bc_return_value(emit_t *emit) { - emit_bc_pre(emit, -1); + emit_write_bytecode_byte(emit, -1, MP_BC_RETURN_VALUE); emit->last_emit_was_return_value = true; - emit_write_bytecode_byte(emit, MP_BC_RETURN_VALUE); } void mp_emit_bc_raise_varargs(emit_t *emit, mp_uint_t n_args) { + MP_STATIC_ASSERT(MP_BC_RAISE_LAST + 1 == MP_BC_RAISE_OBJ); + MP_STATIC_ASSERT(MP_BC_RAISE_LAST + 2 == MP_BC_RAISE_FROM); assert(n_args <= 2); - emit_bc_pre(emit, -n_args); - emit_write_bytecode_byte_byte(emit, MP_BC_RAISE_VARARGS, n_args); + emit_write_bytecode_byte(emit, -n_args, MP_BC_RAISE_LAST + n_args); } void mp_emit_bc_yield(emit_t *emit, int kind) { MP_STATIC_ASSERT(MP_BC_YIELD_VALUE + 1 == MP_BC_YIELD_FROM); - emit_bc_pre(emit, -kind); + emit_write_bytecode_byte(emit, -kind, MP_BC_YIELD_VALUE + kind); emit->scope->scope_flags |= MP_SCOPE_FLAG_GENERATOR; - emit_write_bytecode_byte(emit, MP_BC_YIELD_VALUE + kind); } void mp_emit_bc_start_except_handler(emit_t *emit) { diff --git a/python/src/py/emitglue.c b/python/src/py/emitglue.c index c073258f0..d30a1e674 100644 --- a/python/src/py/emitglue.c +++ b/python/src/py/emitglue.c @@ -34,6 +34,7 @@ #include "py/emitglue.h" #include "py/runtime0.h" #include "py/bc.h" +#include "py/profile.h" #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) @@ -52,6 +53,9 @@ mp_uint_t mp_verbose_flag = 0; mp_raw_code_t *mp_emit_glue_new_raw_code(void) { mp_raw_code_t *rc = m_new0(mp_raw_code_t, 1); rc->kind = MP_CODE_RESERVED; + #if MICROPY_PY_SYS_SETTRACE + rc->line_of_definition = 0; + #endif return rc; } @@ -75,6 +79,11 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, rc->n_raw_code = n_raw_code; #endif + #if MICROPY_PY_SYS_SETTRACE + mp_bytecode_prelude_t *prelude = &rc->prelude; + mp_prof_extract_prelude(code, prelude); + #endif + #ifdef DEBUG_PRINT #if !MICROPY_DEBUG_PRINTERS const size_t len = 0; @@ -88,7 +97,7 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, #endif } -#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM +#if MICROPY_EMIT_MACHINE_CODE void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, const mp_uint_t *const_table, #if MICROPY_PERSISTENT_CODE_SAVE uint16_t prelude_offset, @@ -172,6 +181,12 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) { ((mp_obj_base_t*)MP_OBJ_TO_PTR(fun))->type = &mp_type_gen_wrap; } + + #if MICROPY_PY_SYS_SETTRACE + mp_obj_fun_bc_t *self_fun = (mp_obj_fun_bc_t *)MP_OBJ_TO_PTR(fun); + self_fun->rc = rc; + #endif + break; } diff --git a/python/src/py/emitglue.h b/python/src/py/emitglue.h index 058f06018..a5411dc2e 100644 --- a/python/src/py/emitglue.h +++ b/python/src/py/emitglue.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_PY_EMITGLUE_H #include "py/obj.h" +#include "py/bc.h" // These variables and functions glue the code emitters to the runtime. @@ -63,13 +64,21 @@ typedef struct _mp_raw_code_t { size_t fun_data_len; uint16_t n_obj; uint16_t n_raw_code; - #if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM + #if MICROPY_PY_SYS_SETTRACE + mp_bytecode_prelude_t prelude; + // line_of_definition is a Python source line where the raw_code was + // created e.g. MP_BC_MAKE_FUNCTION. This is different from lineno info + // stored in prelude, which provides line number for first statement of + // a function. Required to properly implement "call" trace event. + mp_uint_t line_of_definition; + #endif + #if MICROPY_EMIT_MACHINE_CODE uint16_t prelude_offset; uint16_t n_qstr; mp_qstr_link_entry_t *qstr_link; #endif #endif - #if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM + #if MICROPY_EMIT_MACHINE_CODE mp_uint_t type_sig; // for viper, compressed as 2-bit types; ret is MSB, then arg0, arg1, etc #endif } mp_raw_code_t; diff --git a/python/src/py/emitnative.c b/python/src/py/emitnative.c index f123ecbb5..07b984b78 100644 --- a/python/src/py/emitnative.c +++ b/python/src/py/emitnative.c @@ -47,7 +47,8 @@ #include #include "py/emit.h" -#include "py/bc.h" +#include "py/nativeglue.h" +#include "py/objstr.h" #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) @@ -57,7 +58,7 @@ #endif // wrapper around everything in this file -#if N_X64 || N_X86 || N_THUMB || N_ARM || N_XTENSA +#if N_X64 || N_X86 || N_THUMB || N_ARM || N_XTENSA || N_XTENSAWIN // C stack layout for native functions: // 0: nlr_buf_t [optional] @@ -80,6 +81,30 @@ // locals (reversed, L0 at end) | // (L0-L2 may be in regs instead) +// Native emitter needs to know the following sizes and offsets of C structs (on the target): +#if MICROPY_DYNAMIC_COMPILER +#define SIZEOF_NLR_BUF (2 + mp_dynamic_compiler.nlr_buf_num_regs + 1) // the +1 is conservative in case MICROPY_ENABLE_PYSTACK enabled +#else +#define SIZEOF_NLR_BUF (sizeof(nlr_buf_t) / sizeof(uintptr_t)) +#endif +#define SIZEOF_CODE_STATE (sizeof(mp_code_state_t) / sizeof(uintptr_t)) +#define OFFSETOF_CODE_STATE_STATE (offsetof(mp_code_state_t, state) / sizeof(uintptr_t)) +#define OFFSETOF_CODE_STATE_FUN_BC (offsetof(mp_code_state_t, fun_bc) / sizeof(uintptr_t)) +#define OFFSETOF_CODE_STATE_IP (offsetof(mp_code_state_t, ip) / sizeof(uintptr_t)) +#define OFFSETOF_CODE_STATE_SP (offsetof(mp_code_state_t, sp) / sizeof(uintptr_t)) +#define OFFSETOF_OBJ_FUN_BC_GLOBALS (offsetof(mp_obj_fun_bc_t, globals) / sizeof(uintptr_t)) +#define OFFSETOF_OBJ_FUN_BC_BYTECODE (offsetof(mp_obj_fun_bc_t, bytecode) / sizeof(uintptr_t)) +#define OFFSETOF_OBJ_FUN_BC_CONST_TABLE (offsetof(mp_obj_fun_bc_t, const_table) / sizeof(uintptr_t)) + +// If not already defined, set parent args to same as child call registers +#ifndef REG_PARENT_RET +#define REG_PARENT_RET REG_RET +#define REG_PARENT_ARG_1 REG_ARG_1 +#define REG_PARENT_ARG_2 REG_ARG_2 +#define REG_PARENT_ARG_3 REG_ARG_3 +#define REG_PARENT_ARG_4 REG_ARG_4 +#endif + // Word index of nlr_buf_t.ret_val #define NLR_BUF_IDX_RET_VAL (1) @@ -101,9 +126,9 @@ #define LOCAL_IDX_EXC_HANDLER_PC(emit) (NLR_BUF_IDX_LOCAL_1) #define LOCAL_IDX_EXC_HANDLER_UNWIND(emit) (NLR_BUF_IDX_LOCAL_2) #define LOCAL_IDX_RET_VAL(emit) (NLR_BUF_IDX_LOCAL_3) -#define LOCAL_IDX_FUN_OBJ(emit) ((emit)->code_state_start + offsetof(mp_code_state_t, fun_bc) / sizeof(uintptr_t)) -#define LOCAL_IDX_OLD_GLOBALS(emit) ((emit)->code_state_start + offsetof(mp_code_state_t, ip) / sizeof(uintptr_t)) -#define LOCAL_IDX_GEN_PC(emit) ((emit)->code_state_start + offsetof(mp_code_state_t, ip) / sizeof(uintptr_t)) +#define LOCAL_IDX_FUN_OBJ(emit) ((emit)->code_state_start + OFFSETOF_CODE_STATE_FUN_BC) +#define LOCAL_IDX_OLD_GLOBALS(emit) ((emit)->code_state_start + OFFSETOF_CODE_STATE_IP) +#define LOCAL_IDX_GEN_PC(emit) ((emit)->code_state_start + OFFSETOF_CODE_STATE_IP) #define LOCAL_IDX_LOCAL_VAR(emit, local_num) ((emit)->stack_start + (emit)->n_state - 1 - (local_num)) #define REG_GENERATOR_STATE (REG_LOCAL_3) @@ -194,6 +219,7 @@ struct _emit_t { uint16_t code_state_start; uint16_t stack_start; int stack_size; + uint16_t n_cell; uint16_t const_table_cur_obj; uint16_t const_table_num_obj; @@ -309,7 +335,11 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop emit->pass = pass; emit->do_viper_types = scope->emit_options == MP_EMIT_OPT_VIPER; emit->stack_size = 0; + #if N_PRELUDE_AS_BYTES_OBJ + emit->const_table_cur_obj = emit->do_viper_types ? 0 : 1; // reserve first obj for prelude bytes obj + #else emit->const_table_cur_obj = 0; + #endif emit->const_table_cur_raw_code = 0; #if MICROPY_PERSISTENT_CODE_SAVE emit->qstr_link_cur = 0; @@ -364,7 +394,7 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop // Work out start of code state (mp_code_state_t or reduced version for viper) emit->code_state_start = 0; if (NEED_GLOBAL_EXC_HANDLER(emit)) { - emit->code_state_start = sizeof(nlr_buf_t) / sizeof(uintptr_t); + emit->code_state_start = SIZEOF_NLR_BUF; } if (emit->do_viper_types) { @@ -398,16 +428,16 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop ASM_ENTRY(emit->as, emit->stack_start + emit->n_state - num_locals_in_regs); #if N_X86 - asm_x86_mov_arg_to_r32(emit->as, 0, REG_ARG_1); + asm_x86_mov_arg_to_r32(emit->as, 0, REG_PARENT_ARG_1); #endif // Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_ARG_1, offsetof(mp_obj_fun_bc_t, const_table) / sizeof(uintptr_t)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONST_TABLE); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_LOCAL_3, 0); // Store function object (passed as first arg) to stack if needed if (NEED_FUN_OBJ(emit)) { - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_ARG_1); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_PARENT_ARG_1); } // Put n_args in REG_ARG_1, n_kw in REG_ARG_2, args array in REG_LOCAL_3 @@ -416,9 +446,9 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop asm_x86_mov_arg_to_r32(emit->as, 2, REG_ARG_2); asm_x86_mov_arg_to_r32(emit->as, 3, REG_LOCAL_3); #else - ASM_MOV_REG_REG(emit->as, REG_ARG_1, REG_ARG_2); - ASM_MOV_REG_REG(emit->as, REG_ARG_2, REG_ARG_3); - ASM_MOV_REG_REG(emit->as, REG_LOCAL_3, REG_ARG_4); + ASM_MOV_REG_REG(emit->as, REG_ARG_1, REG_PARENT_ARG_2); + ASM_MOV_REG_REG(emit->as, REG_ARG_2, REG_PARENT_ARG_3); + ASM_MOV_REG_REG(emit->as, REG_LOCAL_3, REG_PARENT_ARG_4); #endif // Check number of args matches this function, and call mp_arg_check_num_sig if not @@ -458,31 +488,36 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { emit->code_state_start = 0; - emit->stack_start = sizeof(mp_code_state_t) / sizeof(mp_uint_t); + emit->stack_start = SIZEOF_CODE_STATE; + #if N_PRELUDE_AS_BYTES_OBJ + // Load index of prelude bytes object in const_table + mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)(emit->scope->num_pos_args + emit->scope->num_kwonly_args + 1)); + #else mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)emit->prelude_offset); + #endif mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)emit->start_offset); - ASM_ENTRY(emit->as, sizeof(nlr_buf_t) / sizeof(uintptr_t)); + ASM_ENTRY(emit->as, SIZEOF_NLR_BUF); // Put address of code_state into REG_GENERATOR_STATE #if N_X86 asm_x86_mov_arg_to_r32(emit->as, 0, REG_GENERATOR_STATE); #else - ASM_MOV_REG_REG(emit->as, REG_GENERATOR_STATE, REG_ARG_1); + ASM_MOV_REG_REG(emit->as, REG_GENERATOR_STATE, REG_PARENT_ARG_1); #endif // Put throw value into LOCAL_IDX_EXC_VAL slot, for yield/yield-from #if N_X86 - asm_x86_mov_arg_to_r32(emit->as, 1, REG_ARG_2); + asm_x86_mov_arg_to_r32(emit->as, 1, REG_PARENT_ARG_2); #endif - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_ARG_2); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_PARENT_ARG_2); // Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_GENERATOR_STATE, LOCAL_IDX_FUN_OBJ(emit)); - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, offsetof(mp_obj_fun_bc_t, const_table) / sizeof(uintptr_t)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_OBJ_FUN_BC_CONST_TABLE); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_TEMP0, emit->scope->num_pos_args + emit->scope->num_kwonly_args); } else { // The locals and stack start after the code_state structure - emit->stack_start = emit->code_state_start + sizeof(mp_code_state_t) / sizeof(mp_uint_t); + emit->stack_start = emit->code_state_start + SIZEOF_CODE_STATE; // Allocate space on C-stack for code_state structure, which includes state ASM_ENTRY(emit->as, emit->stack_start + emit->n_state); @@ -490,26 +525,49 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop // Prepare incoming arguments for call to mp_setup_code_state #if N_X86 - asm_x86_mov_arg_to_r32(emit->as, 0, REG_ARG_1); - asm_x86_mov_arg_to_r32(emit->as, 1, REG_ARG_2); - asm_x86_mov_arg_to_r32(emit->as, 2, REG_ARG_3); - asm_x86_mov_arg_to_r32(emit->as, 3, REG_ARG_4); + asm_x86_mov_arg_to_r32(emit->as, 0, REG_PARENT_ARG_1); + asm_x86_mov_arg_to_r32(emit->as, 1, REG_PARENT_ARG_2); + asm_x86_mov_arg_to_r32(emit->as, 2, REG_PARENT_ARG_3); + asm_x86_mov_arg_to_r32(emit->as, 3, REG_PARENT_ARG_4); #endif // Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_ARG_1, offsetof(mp_obj_fun_bc_t, const_table) / sizeof(uintptr_t)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONST_TABLE); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_LOCAL_3, emit->scope->num_pos_args + emit->scope->num_kwonly_args); // Set code_state.fun_bc - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_ARG_1); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_PARENT_ARG_1); // Set code_state.ip (offset from start of this function to prelude info) + #if N_PRELUDE_AS_BYTES_OBJ + // Prelude is a bytes object in const_table; store ip = prelude->data - fun_bc->bytecode + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_LOCAL_3, emit->scope->num_pos_args + emit->scope->num_kwonly_args + 1); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_LOCAL_3, offsetof(mp_obj_str_t, data) / sizeof(uintptr_t)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_PARENT_ARG_1, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_BYTECODE); + ASM_SUB_REG_REG(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1); + emit_native_mov_state_reg(emit, emit->code_state_start + OFFSETOF_CODE_STATE_IP, REG_LOCAL_3); + #else // TODO this encoding may change size in the final pass, need to make it fixed - emit_native_mov_state_imm_via(emit, emit->code_state_start + offsetof(mp_code_state_t, ip) / sizeof(uintptr_t), emit->prelude_offset, REG_ARG_1); + emit_native_mov_state_imm_via(emit, emit->code_state_start + OFFSETOF_CODE_STATE_IP, emit->prelude_offset, REG_PARENT_ARG_1); + #endif + + // Set code_state.n_state (only works on little endian targets due to n_state being uint16_t) + emit_native_mov_state_imm_via(emit, emit->code_state_start + offsetof(mp_code_state_t, n_state) / sizeof(uintptr_t), emit->n_state, REG_ARG_1); // Put address of code_state into first arg ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, emit->code_state_start); + // Copy next 3 args if needed + #if REG_ARG_2 != REG_PARENT_ARG_2 + ASM_MOV_REG_REG(emit->as, REG_ARG_2, REG_PARENT_ARG_2); + #endif + #if REG_ARG_3 != REG_PARENT_ARG_3 + ASM_MOV_REG_REG(emit->as, REG_ARG_3, REG_PARENT_ARG_3); + #endif + #if REG_ARG_4 != REG_PARENT_ARG_4 + ASM_MOV_REG_REG(emit->as, REG_ARG_4, REG_PARENT_ARG_4); + #endif + // Call mp_setup_code_state to prepare code_state structure #if N_THUMB asm_thumb_bl_ind(emit->as, MP_F_SETUP_CODE_STATE, ASM_THUMB_REG_R4); @@ -556,22 +614,28 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop } +static inline void emit_native_write_code_info_byte(emit_t *emit, byte val) { + mp_asm_base_data(&emit->as->base, 1, val); +} + STATIC void emit_native_end_pass(emit_t *emit) { emit_native_global_exc_exit(emit); if (!emit->do_viper_types) { emit->prelude_offset = mp_asm_base_get_code_pos(&emit->as->base); - mp_asm_base_data(&emit->as->base, 1, 0x80 | ((emit->n_state >> 7) & 0x7f)); - mp_asm_base_data(&emit->as->base, 1, emit->n_state & 0x7f); - mp_asm_base_data(&emit->as->base, 1, 0); // n_exc_stack - mp_asm_base_data(&emit->as->base, 1, emit->scope->scope_flags); - mp_asm_base_data(&emit->as->base, 1, emit->scope->num_pos_args); - mp_asm_base_data(&emit->as->base, 1, emit->scope->num_kwonly_args); - mp_asm_base_data(&emit->as->base, 1, emit->scope->num_def_pos_args); - // write code info + size_t n_state = emit->n_state; + size_t n_exc_stack = 0; // exc-stack not needed for native code + MP_BC_PRELUDE_SIG_ENCODE(n_state, n_exc_stack, emit->scope, emit_native_write_code_info_byte, emit); + + #if MICROPY_PERSISTENT_CODE + size_t n_info = 4; + #else + size_t n_info = 1; + #endif + MP_BC_PRELUDE_SIZE_ENCODE(n_info, emit->n_cell, emit_native_write_code_info_byte, emit); + #if MICROPY_PERSISTENT_CODE - mp_asm_base_data(&emit->as->base, 1, 5); mp_asm_base_data(&emit->as->base, 1, emit->scope->simple_name); mp_asm_base_data(&emit->as->base, 1, emit->scope->simple_name >> 8); mp_asm_base_data(&emit->as->base, 1, emit->scope->source_file); @@ -581,14 +645,25 @@ STATIC void emit_native_end_pass(emit_t *emit) { #endif // bytecode prelude: initialise closed over variables + size_t cell_start = mp_asm_base_get_code_pos(&emit->as->base); for (int i = 0; i < emit->scope->id_info_len; i++) { id_info_t *id = &emit->scope->id_info[i]; if (id->kind == ID_INFO_KIND_CELL) { - assert(id->local_num < 255); + assert(id->local_num <= 255); mp_asm_base_data(&emit->as->base, 1, id->local_num); // write the local which should be converted to a cell } } - mp_asm_base_data(&emit->as->base, 1, 255); // end of list sentinel + emit->n_cell = mp_asm_base_get_code_pos(&emit->as->base) - cell_start; + + #if N_PRELUDE_AS_BYTES_OBJ + // Prelude bytes object is after qstr arg names and mp_fun_table + size_t table_off = emit->scope->num_pos_args + emit->scope->num_kwonly_args + 1; + if (emit->pass == MP_PASS_EMIT) { + void *buf = emit->as->base.code_base + emit->prelude_offset; + size_t n = emit->as->base.code_offset - emit->prelude_offset; + emit->const_table[table_off] = (uintptr_t)mp_obj_new_bytes(buf, n); + } + #endif } ASM_END_PASS(emit->as); @@ -609,8 +684,11 @@ STATIC void emit_native_end_pass(emit_t *emit) { const_table_alloc += nqstr; } emit->const_table = m_new(mp_uint_t, const_table_alloc); + #if !MICROPY_DYNAMIC_COMPILER // Store mp_fun_table pointer just after qstrs - emit->const_table[nqstr] = (mp_uint_t)(uintptr_t)mp_fun_table; + // (but in dynamic-compiler mode eliminate dependency on mp_fun_table) + emit->const_table[nqstr] = (mp_uint_t)(uintptr_t)&mp_fun_table; + #endif #if MICROPY_PERSISTENT_CODE_SAVE size_t qstr_link_alloc = emit->qstr_link_cur; @@ -1033,7 +1111,7 @@ STATIC void emit_load_reg_with_ptr(emit_t *emit, int reg, mp_uint_t ptr, size_t emit->const_table[table_off] = ptr; } emit_native_mov_reg_state(emit, REG_TEMP0, LOCAL_IDX_FUN_OBJ(emit)); - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, offsetof(mp_obj_fun_bc_t, const_table) / sizeof(uintptr_t)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_OBJ_FUN_BC_CONST_TABLE); ASM_LOAD_REG_REG_OFFSET(emit->as, reg, REG_TEMP0, table_off); } @@ -1090,7 +1168,7 @@ STATIC void emit_native_global_exc_entry(emit_t *emit) { if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) { // Set new globals emit_native_mov_reg_state(emit, REG_ARG_1, LOCAL_IDX_FUN_OBJ(emit)); - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_1, REG_ARG_1, offsetof(mp_obj_fun_bc_t, globals) / sizeof(uintptr_t)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_1, REG_ARG_1, OFFSETOF_OBJ_FUN_BC_GLOBALS); emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS); // Save old globals (or NULL if globals didn't change) @@ -1106,6 +1184,10 @@ STATIC void emit_native_global_exc_entry(emit_t *emit) { // Wrap everything in an nlr context ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, 0); emit_call(emit, MP_F_NLR_PUSH); + #if N_NLR_SETJMP + ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, 2); + emit_call(emit, MP_F_SETJMP); + #endif ASM_JUMP_IF_REG_ZERO(emit->as, REG_RET, start_label, true); } else { // Clear the unwind state @@ -1120,6 +1202,10 @@ STATIC void emit_native_global_exc_entry(emit_t *emit) { ASM_MOV_REG_LOCAL(emit->as, REG_LOCAL_2, LOCAL_IDX_EXC_HANDLER_UNWIND(emit)); ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, 0); emit_call(emit, MP_F_NLR_PUSH); + #if N_NLR_SETJMP + ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, 2); + emit_call(emit, MP_F_SETJMP); + #endif ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_UNWIND(emit), REG_LOCAL_2); ASM_JUMP_IF_REG_NONZERO(emit->as, REG_RET, global_except_label, true); @@ -1130,6 +1216,12 @@ STATIC void emit_native_global_exc_entry(emit_t *emit) { // Global exception handler: check for valid exception handler emit_native_label_assign(emit, global_except_label); + #if N_NLR_SETJMP + // Reload REG_FUN_TABLE, since it may be clobbered by longjmp + emit_native_mov_reg_state(emit, REG_LOCAL_1, LOCAL_IDX_FUN_OBJ(emit)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_1, REG_LOCAL_1, offsetof(mp_obj_fun_bc_t, const_table) / sizeof(uintptr_t)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_LOCAL_1, emit->scope->num_pos_args + emit->scope->num_kwonly_args); + #endif ASM_MOV_REG_LOCAL(emit->as, REG_LOCAL_1, LOCAL_IDX_EXC_HANDLER_PC(emit)); ASM_JUMP_IF_REG_NONZERO(emit->as, REG_LOCAL_1, nlr_label, false); } @@ -1143,10 +1235,10 @@ STATIC void emit_native_global_exc_entry(emit_t *emit) { if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { // Store return value in state[0] ASM_MOV_REG_LOCAL(emit->as, REG_TEMP0, LOCAL_IDX_EXC_VAL(emit)); - ASM_STORE_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_GENERATOR_STATE, offsetof(mp_code_state_t, state) / sizeof(uintptr_t)); + ASM_STORE_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_GENERATOR_STATE, OFFSETOF_CODE_STATE_STATE); // Load return kind - ASM_MOV_REG_IMM(emit->as, REG_RET, MP_VM_RETURN_EXCEPTION); + ASM_MOV_REG_IMM(emit->as, REG_PARENT_RET, MP_VM_RETURN_EXCEPTION); ASM_EXIT(emit->as); } else { @@ -1201,7 +1293,7 @@ STATIC void emit_native_global_exc_exit(emit_t *emit) { } // Load return value - ASM_MOV_REG_LOCAL(emit->as, REG_RET, LOCAL_IDX_RET_VAL(emit)); + ASM_MOV_REG_LOCAL(emit->as, REG_PARENT_RET, LOCAL_IDX_RET_VAL(emit)); } ASM_EXIT(emit->as); @@ -2312,7 +2404,7 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { ASM_ARM_CC_NE, }; asm_arm_setcc_reg(emit->as, REG_RET, ccs[op - MP_BINARY_OP_LESS]); - #elif N_XTENSA + #elif N_XTENSA || N_XTENSAWIN static uint8_t ccs[6] = { ASM_XTENSA_CC_LT, 0x80 | ASM_XTENSA_CC_LT, // for GT we'll swap args @@ -2572,7 +2664,7 @@ STATIC void emit_native_return_value(emit_t *emit) { if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { // Save pointer to current stack position for caller to access return value emit_get_stack_pointer_to_reg_for_pop(emit, REG_TEMP0, 1); - emit_native_mov_state_reg(emit, offsetof(mp_code_state_t, sp) / sizeof(uintptr_t), REG_TEMP0); + emit_native_mov_state_reg(emit, OFFSETOF_CODE_STATE_SP, REG_TEMP0); // Put return type in return value slot ASM_MOV_REG_IMM(emit->as, REG_TEMP0, MP_VM_RETURN_NORMAL); @@ -2589,13 +2681,13 @@ STATIC void emit_native_return_value(emit_t *emit) { if (peek_vtype(emit, 0) == VTYPE_PTR_NONE) { emit_pre_pop_discard(emit); if (return_vtype == VTYPE_PYOBJ) { - emit_native_mov_reg_const(emit, REG_RET, MP_F_CONST_NONE_OBJ); + emit_native_mov_reg_const(emit, REG_PARENT_RET, MP_F_CONST_NONE_OBJ); } else { ASM_MOV_REG_IMM(emit->as, REG_ARG_1, 0); } } else { vtype_kind_t vtype; - emit_pre_pop_reg(emit, &vtype, return_vtype == VTYPE_PYOBJ ? REG_RET : REG_ARG_1); + emit_pre_pop_reg(emit, &vtype, return_vtype == VTYPE_PYOBJ ? REG_PARENT_RET : REG_ARG_1); if (vtype != return_vtype) { EMIT_NATIVE_VIPER_TYPE_ERROR(emit, "return expected '%q' but got '%q'", @@ -2604,15 +2696,18 @@ STATIC void emit_native_return_value(emit_t *emit) { } if (return_vtype != VTYPE_PYOBJ) { emit_call_with_imm_arg(emit, MP_F_CONVERT_NATIVE_TO_OBJ, return_vtype, REG_ARG_2); + #if REG_RET != REG_PARENT_RET + ASM_MOV_REG_REG(emit->as, REG_PARENT_RET, REG_RET); + #endif } } else { vtype_kind_t vtype; - emit_pre_pop_reg(emit, &vtype, REG_RET); + emit_pre_pop_reg(emit, &vtype, REG_PARENT_RET); assert(vtype == VTYPE_PYOBJ); } if (NEED_GLOBAL_EXC_HANDLER(emit)) { // Save return value for the global exception handler to use - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_RET_VAL(emit), REG_RET); + ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_RET_VAL(emit), REG_PARENT_RET); } emit_native_unwind_jump(emit, emit->exit_label, emit->exc_stack_size); emit->last_emit_was_return_value = true; @@ -2655,7 +2750,7 @@ STATIC void emit_native_yield(emit_t *emit, int kind) { // Save pointer to current stack position for caller to access yielded value emit_get_stack_pointer_to_reg_for_pop(emit, REG_TEMP0, 1); - emit_native_mov_state_reg(emit, offsetof(mp_code_state_t, sp) / sizeof(uintptr_t), REG_TEMP0); + emit_native_mov_state_reg(emit, OFFSETOF_CODE_STATE_SP, REG_TEMP0); // Put return type in return value slot ASM_MOV_REG_IMM(emit->as, REG_TEMP0, MP_VM_RETURN_YIELD); diff --git a/python/src/py/emitnx86.c b/python/src/py/emitnx86.c index 7c96c3b82..f0553f068 100644 --- a/python/src/py/emitnx86.c +++ b/python/src/py/emitnx86.c @@ -1,7 +1,7 @@ // x86 specific stuff #include "py/mpconfig.h" -#include "py/runtime0.h" +#include "py/nativeglue.h" #if MICROPY_EMIT_X86 @@ -34,13 +34,11 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { [MP_F_BINARY_OP] = 3, [MP_F_BUILD_TUPLE] = 2, [MP_F_BUILD_LIST] = 2, - [MP_F_LIST_APPEND] = 2, [MP_F_BUILD_MAP] = 1, - [MP_F_STORE_MAP] = 3, - #if MICROPY_PY_BUILTINS_SET [MP_F_BUILD_SET] = 2, [MP_F_STORE_SET] = 2, - #endif + [MP_F_LIST_APPEND] = 2, + [MP_F_STORE_MAP] = 3, [MP_F_MAKE_FUNCTION_FROM_RAW_CODE] = 3, [MP_F_NATIVE_CALL_FUNCTION_N_KW] = 3, [MP_F_CALL_METHOD_N_KW] = 3, @@ -53,20 +51,18 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { [MP_F_IMPORT_NAME] = 3, [MP_F_IMPORT_FROM] = 2, [MP_F_IMPORT_ALL] = 1, - #if MICROPY_PY_BUILTINS_SLICE [MP_F_NEW_SLICE] = 3, - #endif [MP_F_UNPACK_SEQUENCE] = 3, [MP_F_UNPACK_EX] = 3, [MP_F_DELETE_NAME] = 1, [MP_F_DELETE_GLOBAL] = 1, - [MP_F_NEW_CELL] = 1, [MP_F_MAKE_CLOSURE_FROM_RAW_CODE] = 3, [MP_F_ARG_CHECK_NUM_SIG] = 3, [MP_F_SETUP_CODE_STATE] = 4, [MP_F_SMALL_INT_FLOOR_DIVIDE] = 2, [MP_F_SMALL_INT_MODULO] = 2, [MP_F_NATIVE_YIELD_FROM] = 3, + [MP_F_SETJMP] = 1, }; #define N_X86 (1) diff --git a/python/src/py/emitnxtensawin.c b/python/src/py/emitnxtensawin.c new file mode 100644 index 000000000..38d5db13e --- /dev/null +++ b/python/src/py/emitnxtensawin.c @@ -0,0 +1,23 @@ +// Xtensa-Windowed specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_XTENSAWIN + +// this is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#define GENERIC_ASM_API_WIN (1) +#include "py/asmxtensa.h" + +// Word indices of REG_LOCAL_x in nlr_buf_t +#define NLR_BUF_IDX_LOCAL_1 (2 + 4) // a4 +#define NLR_BUF_IDX_LOCAL_2 (2 + 5) // a5 +#define NLR_BUF_IDX_LOCAL_3 (2 + 6) // a6 + +#define N_NLR_SETJMP (1) +#define N_PRELUDE_AS_BYTES_OBJ (1) +#define N_XTENSAWIN (1) +#define EXPORT_FUN(name) emit_native_xtensawin_##name +#include "py/emitnative.c" + +#endif diff --git a/python/src/py/grammar.h b/python/src/py/grammar.h index 5a5b682ac..ca9b5b379 100644 --- a/python/src/py/grammar.h +++ b/python/src/py/grammar.h @@ -55,7 +55,7 @@ DEF_RULE_NC(eval_input_2, and(1), tok(NEWLINE)) // varargslist: vfpdef ['=' test] (',' vfpdef ['=' test])* [',' ['*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef]] | '*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef // vfpdef: NAME -DEF_RULE_NC(decorator, and(4), tok(DEL_AT), rule(dotted_name), opt_rule(trailer_paren), tok(NEWLINE)) +DEF_RULE_NC(decorator, and(4), tok(OP_AT), rule(dotted_name), opt_rule(trailer_paren), tok(NEWLINE)) DEF_RULE_NC(decorators, one_or_more, rule(decorator)) DEF_RULE(decorated, c(decorated), and_ident(2), rule(decorators), rule(decorated_body)) #if MICROPY_PY_ASYNC_AWAIT @@ -96,7 +96,7 @@ DEF_RULE(simple_stmt_2, c(generic_all_nodes), list_with_end, rule(small_stmt), t // small_stmt: expr_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | nonlocal_stmt | assert_stmt // expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) | ('=' (yield_expr|testlist_star_expr))*) // testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] -// augassign: '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=' +// augassign: '+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=' // # For normal assignments, additional restrictions enforced by the interpreter DEF_RULE_NC(small_stmt, or(8), rule(del_stmt), rule(pass_stmt), rule(flow_stmt), rule(import_stmt), rule(global_stmt), rule(nonlocal_stmt), rule(assert_stmt), rule(expr_stmt)) @@ -108,7 +108,7 @@ DEF_RULE_NC(expr_stmt_assign, and_ident(2), tok(DEL_EQUAL), rule(expr_stmt_6)) DEF_RULE_NC(expr_stmt_6, or(2), rule(yield_expr), rule(testlist_star_expr)) DEF_RULE(testlist_star_expr, c(generic_tuple), list_with_end, rule(testlist_star_expr_2), tok(DEL_COMMA)) DEF_RULE_NC(testlist_star_expr_2, or(2), rule(star_expr), rule(test)) -DEF_RULE_NC(augassign, or(12), tok(DEL_PLUS_EQUAL), tok(DEL_MINUS_EQUAL), tok(DEL_STAR_EQUAL), tok(DEL_SLASH_EQUAL), tok(DEL_PERCENT_EQUAL), tok(DEL_AMPERSAND_EQUAL), tok(DEL_PIPE_EQUAL), tok(DEL_CARET_EQUAL), tok(DEL_DBL_LESS_EQUAL), tok(DEL_DBL_MORE_EQUAL), tok(DEL_DBL_STAR_EQUAL), tok(DEL_DBL_SLASH_EQUAL)) +DEF_RULE_NC(augassign, or(13), tok(DEL_PLUS_EQUAL), tok(DEL_MINUS_EQUAL), tok(DEL_STAR_EQUAL), tok(DEL_AT_EQUAL), tok(DEL_SLASH_EQUAL), tok(DEL_PERCENT_EQUAL), tok(DEL_AMPERSAND_EQUAL), tok(DEL_PIPE_EQUAL), tok(DEL_CARET_EQUAL), tok(DEL_DBL_LESS_EQUAL), tok(DEL_DBL_MORE_EQUAL), tok(DEL_DBL_STAR_EQUAL), tok(DEL_DBL_SLASH_EQUAL)) // del_stmt: 'del' exprlist // pass_stmt: 'pass' @@ -226,7 +226,7 @@ DEF_RULE(lambdef_nocond, c(lambdef), and_blank(4), tok(KW_LAMBDA), opt_rule(vara // and_expr: shift_expr ('&' shift_expr)* // shift_expr: arith_expr (('<<'|'>>') arith_expr)* // arith_expr: term (('+'|'-') term)* -// term: factor (('*'|'/'|'%'|'//') factor)* +// term: factor (('*'|'@'|'/'|'%'|'//') factor)* // factor: ('+'|'-'|'~') factor | power // power: atom_expr ['**' factor] // atom_expr: 'await' atom trailer* | atom trailer* @@ -249,7 +249,7 @@ DEF_RULE_NC(shift_op, or(2), tok(OP_DBL_LESS), tok(OP_DBL_MORE)) DEF_RULE(arith_expr, c(term), list, rule(term), rule(arith_op)) DEF_RULE_NC(arith_op, or(2), tok(OP_PLUS), tok(OP_MINUS)) DEF_RULE(term, c(term), list, rule(factor), rule(term_op)) -DEF_RULE_NC(term_op, or(4), tok(OP_STAR), tok(OP_SLASH), tok(OP_PERCENT), tok(OP_DBL_SLASH)) +DEF_RULE_NC(term_op, or(5), tok(OP_STAR), tok(OP_AT), tok(OP_SLASH), tok(OP_PERCENT), tok(OP_DBL_SLASH)) DEF_RULE_NC(factor, or(2), rule(factor_2), rule(power)) DEF_RULE(factor_2, c(factor_2), and_ident(2), rule(factor_op), rule(factor)) DEF_RULE_NC(factor_op, or(3), tok(OP_PLUS), tok(OP_MINUS), tok(OP_TILDE)) diff --git a/python/src/py/lexer.c b/python/src/py/lexer.c index e161700b1..5f8adda91 100644 --- a/python/src/py/lexer.c +++ b/python/src/py/lexer.c @@ -174,7 +174,7 @@ STATIC void indent_pop(mp_lexer_t *lex) { // this means if the start of two ops are the same then they are equal til the last char STATIC const char *const tok_enc = - "()[]{},:;@~" // singles + "()[]{},:;~" // singles " >= >> >>= "*e=c*e=" // * *= ** **= @@ -185,6 +185,7 @@ STATIC const char *const tok_enc = "/e=c/e=" // / /= // //= "%e=" // % %= "^e=" // ^ ^= + "@e=" // @ @= "=e=" // = == "!."; // start of special cases: != . ... @@ -193,7 +194,7 @@ STATIC const uint8_t tok_enc_kind[] = { MP_TOKEN_DEL_PAREN_OPEN, MP_TOKEN_DEL_PAREN_CLOSE, MP_TOKEN_DEL_BRACKET_OPEN, MP_TOKEN_DEL_BRACKET_CLOSE, MP_TOKEN_DEL_BRACE_OPEN, MP_TOKEN_DEL_BRACE_CLOSE, - MP_TOKEN_DEL_COMMA, MP_TOKEN_DEL_COLON, MP_TOKEN_DEL_SEMICOLON, MP_TOKEN_DEL_AT, MP_TOKEN_OP_TILDE, + MP_TOKEN_DEL_COMMA, MP_TOKEN_DEL_COLON, MP_TOKEN_DEL_SEMICOLON, MP_TOKEN_OP_TILDE, MP_TOKEN_OP_LESS, MP_TOKEN_OP_LESS_EQUAL, MP_TOKEN_OP_DBL_LESS, MP_TOKEN_DEL_DBL_LESS_EQUAL, MP_TOKEN_OP_MORE, MP_TOKEN_OP_MORE_EQUAL, MP_TOKEN_OP_DBL_MORE, MP_TOKEN_DEL_DBL_MORE_EQUAL, @@ -205,6 +206,7 @@ STATIC const uint8_t tok_enc_kind[] = { MP_TOKEN_OP_SLASH, MP_TOKEN_DEL_SLASH_EQUAL, MP_TOKEN_OP_DBL_SLASH, MP_TOKEN_DEL_DBL_SLASH_EQUAL, MP_TOKEN_OP_PERCENT, MP_TOKEN_DEL_PERCENT_EQUAL, MP_TOKEN_OP_CARET, MP_TOKEN_DEL_CARET_EQUAL, + MP_TOKEN_OP_AT, MP_TOKEN_DEL_AT_EQUAL, MP_TOKEN_DEL_EQUAL, MP_TOKEN_OP_DBL_EQUAL, }; diff --git a/python/src/py/lexer.h b/python/src/py/lexer.h index a29709107..b9f97013a 100644 --- a/python/src/py/lexer.h +++ b/python/src/py/lexer.h @@ -96,25 +96,45 @@ typedef enum _mp_token_kind_t { MP_TOKEN_KW_WITH, MP_TOKEN_KW_YIELD, + MP_TOKEN_OP_TILDE, + + // Order of these 6 matches corresponding mp_binary_op_t operator + MP_TOKEN_OP_LESS, + MP_TOKEN_OP_MORE, + MP_TOKEN_OP_DBL_EQUAL, + MP_TOKEN_OP_LESS_EQUAL, + MP_TOKEN_OP_MORE_EQUAL, + MP_TOKEN_OP_NOT_EQUAL, + + // Order of these 13 matches corresponding mp_binary_op_t operator + MP_TOKEN_OP_PIPE, + MP_TOKEN_OP_CARET, + MP_TOKEN_OP_AMPERSAND, + MP_TOKEN_OP_DBL_LESS, + MP_TOKEN_OP_DBL_MORE, MP_TOKEN_OP_PLUS, MP_TOKEN_OP_MINUS, MP_TOKEN_OP_STAR, - MP_TOKEN_OP_DBL_STAR, - MP_TOKEN_OP_SLASH, + MP_TOKEN_OP_AT, MP_TOKEN_OP_DBL_SLASH, + MP_TOKEN_OP_SLASH, MP_TOKEN_OP_PERCENT, - MP_TOKEN_OP_LESS, - MP_TOKEN_OP_DBL_LESS, - MP_TOKEN_OP_MORE, - MP_TOKEN_OP_DBL_MORE, - MP_TOKEN_OP_AMPERSAND, - MP_TOKEN_OP_PIPE, - MP_TOKEN_OP_CARET, - MP_TOKEN_OP_TILDE, - MP_TOKEN_OP_LESS_EQUAL, - MP_TOKEN_OP_MORE_EQUAL, - MP_TOKEN_OP_DBL_EQUAL, - MP_TOKEN_OP_NOT_EQUAL, + MP_TOKEN_OP_DBL_STAR, + + // Order of these 13 matches corresponding mp_binary_op_t operator + MP_TOKEN_DEL_PIPE_EQUAL, + MP_TOKEN_DEL_CARET_EQUAL, + MP_TOKEN_DEL_AMPERSAND_EQUAL, + MP_TOKEN_DEL_DBL_LESS_EQUAL, + MP_TOKEN_DEL_DBL_MORE_EQUAL, + MP_TOKEN_DEL_PLUS_EQUAL, + MP_TOKEN_DEL_MINUS_EQUAL, + MP_TOKEN_DEL_STAR_EQUAL, + MP_TOKEN_DEL_AT_EQUAL, + MP_TOKEN_DEL_DBL_SLASH_EQUAL, + MP_TOKEN_DEL_SLASH_EQUAL, + MP_TOKEN_DEL_PERCENT_EQUAL, + MP_TOKEN_DEL_DBL_STAR_EQUAL, MP_TOKEN_DEL_PAREN_OPEN, MP_TOKEN_DEL_PAREN_CLOSE, @@ -126,20 +146,7 @@ typedef enum _mp_token_kind_t { MP_TOKEN_DEL_COLON, MP_TOKEN_DEL_PERIOD, MP_TOKEN_DEL_SEMICOLON, - MP_TOKEN_DEL_AT, MP_TOKEN_DEL_EQUAL, - MP_TOKEN_DEL_PLUS_EQUAL, - MP_TOKEN_DEL_MINUS_EQUAL, - MP_TOKEN_DEL_STAR_EQUAL, - MP_TOKEN_DEL_SLASH_EQUAL, - MP_TOKEN_DEL_DBL_SLASH_EQUAL, - MP_TOKEN_DEL_PERCENT_EQUAL, - MP_TOKEN_DEL_AMPERSAND_EQUAL, - MP_TOKEN_DEL_PIPE_EQUAL, - MP_TOKEN_DEL_CARET_EQUAL, - MP_TOKEN_DEL_DBL_MORE_EQUAL, - MP_TOKEN_DEL_DBL_LESS_EQUAL, - MP_TOKEN_DEL_DBL_STAR_EQUAL, MP_TOKEN_DEL_MINUS_MORE, } mp_token_kind_t; @@ -177,8 +184,6 @@ void mp_lexer_to_next(mp_lexer_t *lex); // platform specific import function; must be implemented for a specific port // TODO tidy up, rename, or put elsewhere -//mp_lexer_t *mp_import_open_file(qstr mod_name); - typedef enum { MP_IMPORT_STAT_NO_EXIST, MP_IMPORT_STAT_DIR, diff --git a/python/src/py/makeqstrdata.py b/python/src/py/makeqstrdata.py index 060ebb7fd..7799be0a9 100644 --- a/python/src/py/makeqstrdata.py +++ b/python/src/py/makeqstrdata.py @@ -279,9 +279,11 @@ def parse_input_headers(infiles): # get the qstr value qstr = match.group(1) - # special case to specify control characters + # special cases to specify control characters if qstr == '\\n': qstr = '\n' + elif qstr == '\\r\\n': + qstr = '\r\n' # work out the corresponding qstr name ident = qstr_escape(qstr) @@ -337,7 +339,7 @@ def print_qstr_data(qcfgs, qstrs): print('') # add NULL qstr with no hash or data - print('QDEF(MP_QSTR_NULL, (const byte*)"%s%s" "")' % ('\\x00' * cfg_bytes_hash, '\\x00' * cfg_bytes_len)) + print('QDEF(MP_QSTRnull, (const byte*)"%s%s" "")' % ('\\x00' * cfg_bytes_hash, '\\x00' * cfg_bytes_len)) # go through each qstr and print it out for order, ident, qstr in sorted(qstrs.values(), key=lambda x: x[0]): diff --git a/python/src/py/modarray.c b/python/src/py/modarray.c index de84fc858..b459a8375 100644 --- a/python/src/py/modarray.c +++ b/python/src/py/modarray.c @@ -29,17 +29,17 @@ #if MICROPY_PY_ARRAY STATIC const mp_rom_map_elem_t mp_module_array_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_array) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uarray) }, { MP_ROM_QSTR(MP_QSTR_array), MP_ROM_PTR(&mp_type_array) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_array_globals, mp_module_array_globals_table); -const mp_obj_module_t mp_module_array = { +const mp_obj_module_t mp_module_uarray = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_array_globals, }; -MP_REGISTER_MODULE(MP_QSTR_array, mp_module_array, MICROPY_PY_ARRAY); +MP_REGISTER_MODULE(MP_QSTR_uarray, mp_module_uarray, MICROPY_PY_ARRAY); #endif diff --git a/python/src/py/modio.c b/python/src/py/modio.c index 0f4a4326c..94ef5d42e 100644 --- a/python/src/py/modio.c +++ b/python/src/py/modio.c @@ -208,15 +208,8 @@ STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) { // package parameter being None, the path_in is interpreted as a // raw path. if (package_in != mp_const_none) { - mp_obj_t args[5]; - args[0] = package_in; - args[1] = mp_const_none; // TODO should be globals - args[2] = mp_const_none; // TODO should be locals - args[3] = mp_const_true; // Pass sentinel "non empty" value to force returning of leaf module - args[4] = MP_OBJ_NEW_SMALL_INT(0); - - // TODO lookup __import__ and call that instead of going straight to builtin implementation - mp_obj_t pkg = mp_builtin___import__(5, args); + // Pass "True" as sentinel value in fromlist to force returning of leaf module + mp_obj_t pkg = mp_import_name(mp_obj_str_get_qstr(package_in), mp_const_true, MP_OBJ_NEW_SMALL_INT(0)); mp_obj_t dest[2]; mp_load_method_maybe(pkg, MP_QSTR___path__, dest); diff --git a/python/src/py/modmath.c b/python/src/py/modmath.c index d106f240c..35bb44bea 100644 --- a/python/src/py/modmath.c +++ b/python/src/py/modmath.c @@ -171,6 +171,42 @@ MATH_FUN_1(lgamma, lgamma) #endif //TODO: fsum +#if MICROPY_PY_MATH_ISCLOSE +STATIC mp_obj_t mp_math_isclose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_a, ARG_b, ARG_rel_tol, ARG_abs_tol }; + static const mp_arg_t allowed_args[] = { + {MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_rel_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_abs_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(0)}}, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + const mp_float_t a = mp_obj_get_float(args[ARG_a].u_obj); + const mp_float_t b = mp_obj_get_float(args[ARG_b].u_obj); + const mp_float_t rel_tol = args[ARG_rel_tol].u_obj == MP_OBJ_NULL + ? (mp_float_t)1e-9 : mp_obj_get_float(args[ARG_rel_tol].u_obj); + const mp_float_t abs_tol = mp_obj_get_float(args[ARG_abs_tol].u_obj); + if (rel_tol < (mp_float_t)0.0 || abs_tol < (mp_float_t)0.0) { + math_error(); + } + if (a == b) { + return mp_const_true; + } + const mp_float_t difference = MICROPY_FLOAT_C_FUN(fabs)(a - b); + if (isinf(difference)) { // Either a or b is inf + return mp_const_false; + } + if ((difference <= abs_tol) || + (difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * a)) || + (difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * b))) { + return mp_const_true; + } + return mp_const_false; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_math_isclose_obj, 2, mp_math_isclose); +#endif + // Function that takes a variable number of arguments // log(x[, base]) @@ -335,6 +371,9 @@ STATIC const mp_rom_map_elem_t mp_module_math_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&mp_math_isfinite_obj) }, { MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&mp_math_isinf_obj) }, { MP_ROM_QSTR(MP_QSTR_isnan), MP_ROM_PTR(&mp_math_isnan_obj) }, + #if MICROPY_PY_MATH_ISCLOSE + { MP_ROM_QSTR(MP_QSTR_isclose), MP_ROM_PTR(&mp_math_isclose_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_trunc), MP_ROM_PTR(&mp_math_trunc_obj) }, { MP_ROM_QSTR(MP_QSTR_radians), MP_ROM_PTR(&mp_math_radians_obj) }, { MP_ROM_QSTR(MP_QSTR_degrees), MP_ROM_PTR(&mp_math_degrees_obj) }, diff --git a/python/src/py/modmicropython.c b/python/src/py/modmicropython.c index 864d1a5c5..8d36697f1 100644 --- a/python/src/py/modmicropython.c +++ b/python/src/py/modmicropython.c @@ -150,7 +150,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_micropython_kbd_intr_obj, mp_micropython_kbd #if MICROPY_ENABLE_SCHEDULER STATIC mp_obj_t mp_micropython_schedule(mp_obj_t function, mp_obj_t arg) { if (!mp_sched_schedule(function, arg)) { - mp_raise_msg(&mp_type_RuntimeError, "schedule stack full"); + mp_raise_msg(&mp_type_RuntimeError, "schedule queue full"); } return mp_const_none; } diff --git a/python/src/py/modstruct.c b/python/src/py/modstruct.c index 8617a8e0d..36af4260e 100644 --- a/python/src/py/modstruct.c +++ b/python/src/py/modstruct.c @@ -97,7 +97,7 @@ STATIC size_t calc_size_items(const char *fmt, size_t *total_sz) { size += cnt; } else { total_cnt += cnt; - mp_uint_t align; + size_t align; size_t sz = mp_binary_get_size(fmt_type, *fmt, &align); while (cnt--) { // Apply alignment @@ -146,6 +146,7 @@ STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) { } p += offset; } + byte *p_base = p; // Check that the input buffer is big enough to unpack all the values if (p + total_sz > end_p) { @@ -164,7 +165,7 @@ STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) { res->items[i++] = item; } else { while (cnt--) { - item = mp_binary_get_val(fmt_type, *fmt, &p); + item = mp_binary_get_val(fmt_type, *fmt, p_base, &p); res->items[i++] = item; } } @@ -179,6 +180,7 @@ STATIC void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, c const char *fmt = mp_obj_str_get_str(fmt_in); char fmt_type = get_fmt_type(&fmt); + byte *p_base = p; size_t i; for (i = 0; i < n_args;) { mp_uint_t cnt = 1; @@ -203,7 +205,7 @@ STATIC void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, c } else { // If we run out of args then we just finish; CPython would raise struct.error while (cnt-- && i < n_args) { - mp_binary_set_val(fmt_type, *fmt, args[i++], &p); + mp_binary_set_val(fmt_type, *fmt, args[i++], p_base, &p); } } fmt++; diff --git a/python/src/py/modsys.c b/python/src/py/modsys.c index 343451732..29fac7c31 100644 --- a/python/src/py/modsys.c +++ b/python/src/py/modsys.c @@ -34,6 +34,12 @@ #include "py/stream.h" #include "py/smallint.h" #include "py/runtime.h" +#include "py/persistentcode.h" + +#if MICROPY_PY_SYS_SETTRACE +#include "py/objmodule.h" +#include "py/profile.h" +#endif #if MICROPY_PY_SYS @@ -61,22 +67,36 @@ STATIC const mp_obj_tuple_t mp_sys_implementation_version_info_obj = { 3, { I(MICROPY_VERSION_MAJOR), I(MICROPY_VERSION_MINOR), I(MICROPY_VERSION_MICRO) } }; +#if MICROPY_PERSISTENT_CODE_LOAD +#define SYS_IMPLEMENTATION_ELEMS \ + MP_ROM_QSTR(MP_QSTR_micropython), \ + MP_ROM_PTR(&mp_sys_implementation_version_info_obj), \ + MP_ROM_INT(MPY_FILE_HEADER_INT) +#else +#define SYS_IMPLEMENTATION_ELEMS \ + MP_ROM_QSTR(MP_QSTR_micropython), \ + MP_ROM_PTR(&mp_sys_implementation_version_info_obj) +#endif #if MICROPY_PY_ATTRTUPLE -STATIC const qstr impl_fields[] = { MP_QSTR_name, MP_QSTR_version }; +STATIC const qstr impl_fields[] = { + MP_QSTR_name, + MP_QSTR_version, + #if MICROPY_PERSISTENT_CODE_LOAD + MP_QSTR_mpy, + #endif +}; STATIC MP_DEFINE_ATTRTUPLE( mp_sys_implementation_obj, impl_fields, - 2, - MP_ROM_QSTR(MP_QSTR_micropython), - MP_ROM_PTR(&mp_sys_implementation_version_info_obj) + 2 + MICROPY_PERSISTENT_CODE_LOAD, + SYS_IMPLEMENTATION_ELEMS ); #else STATIC const mp_rom_obj_tuple_t mp_sys_implementation_obj = { {&mp_type_tuple}, - 2, + 2 + MICROPY_PERSISTENT_CODE_LOAD, { - MP_ROM_QSTR(MP_QSTR_micropython), - MP_ROM_PTR(&mp_sys_implementation_version_info_obj), + SYS_IMPLEMENTATION_ELEMS } }; #endif @@ -146,6 +166,24 @@ STATIC mp_obj_t mp_sys_getsizeof(mp_obj_t obj) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_getsizeof_obj, mp_sys_getsizeof); #endif +#if MICROPY_PY_SYS_ATEXIT +// atexit(callback): Callback is called when sys.exit is called. +STATIC mp_obj_t mp_sys_atexit(mp_obj_t obj) { + mp_obj_t old = MP_STATE_VM(sys_exitfunc); + MP_STATE_VM(sys_exitfunc) = obj; + return old; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_atexit_obj, mp_sys_atexit); +#endif + +#if MICROPY_PY_SYS_SETTRACE +// settrace(tracefunc): Set the system’s trace function. +STATIC mp_obj_t mp_sys_settrace(mp_obj_t obj) { + return mp_prof_settrace(obj); +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_settrace_obj, mp_sys_settrace); +#endif // MICROPY_PY_SYS_SETTRACE + STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys) }, @@ -180,6 +218,10 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&mp_sys_exit_obj) }, #endif + #if MICROPY_PY_SYS_SETTRACE + { MP_ROM_QSTR(MP_QSTR_settrace), MP_ROM_PTR(&mp_sys_settrace_obj) }, + #endif + #if MICROPY_PY_SYS_STDFILES { MP_ROM_QSTR(MP_QSTR_stdin), MP_ROM_PTR(&mp_sys_stdin_obj) }, { MP_ROM_QSTR(MP_QSTR_stdout), MP_ROM_PTR(&mp_sys_stdout_obj) }, @@ -201,6 +243,9 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { */ { MP_ROM_QSTR(MP_QSTR_print_exception), MP_ROM_PTR(&mp_sys_print_exception_obj) }, + #if MICROPY_PY_SYS_ATEXIT + { MP_ROM_QSTR(MP_QSTR_atexit), MP_ROM_PTR(&mp_sys_atexit_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_sys_globals, mp_module_sys_globals_table); diff --git a/python/src/py/moduerrno.c b/python/src/py/moduerrno.c index de66c941b..0e0a3f008 100644 --- a/python/src/py/moduerrno.c +++ b/python/src/py/moduerrno.c @@ -104,7 +104,7 @@ qstr mp_errno_to_str(mp_obj_t errno_val) { // We have the errorcode dict so can do a lookup using the hash map mp_map_elem_t *elem = mp_map_lookup((mp_map_t*)&errorcode_dict.map, errno_val, MP_MAP_LOOKUP); if (elem == NULL) { - return MP_QSTR_NULL; + return MP_QSTRnull; } else { return MP_OBJ_QSTR_VALUE(elem->value); } @@ -115,7 +115,7 @@ qstr mp_errno_to_str(mp_obj_t errno_val) { return MP_OBJ_QSTR_VALUE(mp_module_uerrno_globals_table[i].key); } } - return MP_QSTR_NULL; + return MP_QSTRnull; #endif } diff --git a/python/src/py/mpconfig.h b/python/src/py/mpconfig.h index 219f8de44..1e786f753 100644 --- a/python/src/py/mpconfig.h +++ b/python/src/py/mpconfig.h @@ -28,7 +28,7 @@ // Current version of MicroPython #define MICROPY_VERSION_MAJOR 1 -#define MICROPY_VERSION_MINOR 11 +#define MICROPY_VERSION_MINOR 12 #define MICROPY_VERSION_MICRO 0 // Combined version as a 32-bit number for convenience @@ -323,12 +323,23 @@ #define MICROPY_EMIT_INLINE_XTENSA (0) #endif +// Whether to emit Xtensa-Windowed native code +#ifndef MICROPY_EMIT_XTENSAWIN +#define MICROPY_EMIT_XTENSAWIN (0) +#endif + // Convenience definition for whether any native emitter is enabled -#define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA) +#define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA || MICROPY_EMIT_XTENSAWIN) + +// Select prelude-as-bytes-object for certain emitters +#define MICROPY_EMIT_NATIVE_PRELUDE_AS_BYTES_OBJ (MICROPY_EMIT_XTENSAWIN) // Convenience definition for whether any inline assembler emitter is enabled #define MICROPY_EMIT_INLINE_ASM (MICROPY_EMIT_INLINE_THUMB || MICROPY_EMIT_INLINE_XTENSA) +// Convenience definition for whether any native or inline assembler emitter is enabled +#define MICROPY_EMIT_MACHINE_CODE (MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM) + /*****************************************************************************/ /* Compiler configuration */ @@ -338,6 +349,7 @@ #endif // Whether the compiler is dynamically configurable (ie at runtime) +// This will disable the ability to execute native/viper code #ifndef MICROPY_DYNAMIC_COMPILER #define MICROPY_DYNAMIC_COMPILER (0) #endif @@ -1075,6 +1087,11 @@ typedef double mp_float_t; #define MICROPY_PY_MATH_FACTORIAL (0) #endif +// Whether to provide math.isclose function +#ifndef MICROPY_PY_MATH_ISCLOSE +#define MICROPY_PY_MATH_ISCLOSE (0) +#endif + // Whether to provide "cmath" module #ifndef MICROPY_PY_CMATH #define MICROPY_PY_CMATH (0) @@ -1157,6 +1174,16 @@ typedef double mp_float_t; #define MICROPY_PY_SYS_EXIT (1) #endif +// Whether to provide "sys.atexit" function (MicroPython extension) +#ifndef MICROPY_PY_SYS_ATEXIT +#define MICROPY_PY_SYS_ATEXIT (0) +#endif + +// Whether to provide "sys.settrace" function +#ifndef MICROPY_PY_SYS_SETTRACE +#define MICROPY_PY_SYS_SETTRACE (0) +#endif + // Whether to provide "sys.getsizeof" function #ifndef MICROPY_PY_SYS_GETSIZEOF #define MICROPY_PY_SYS_GETSIZEOF (0) @@ -1245,6 +1272,10 @@ typedef double mp_float_t; #define MICROPY_PY_URE (0) #endif +#ifndef MICROPY_PY_URE_DEBUG +#define MICROPY_PY_URE_DEBUG (0) +#endif + #ifndef MICROPY_PY_URE_MATCH_GROUPS #define MICROPY_PY_URE_MATCH_GROUPS (0) #endif @@ -1361,11 +1392,6 @@ typedef double mp_float_t; #define MICROPY_PORT_BUILTIN_MODULES #endif -// Any module weak links - see objmodule.c:mp_builtin_module_weak_links_table. -#ifndef MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS -#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS -#endif - // Additional constant definitions for the compiler - see compile.c:mp_constants_table. #ifndef MICROPY_PORT_CONSTANTS #define MICROPY_PORT_CONSTANTS @@ -1513,6 +1539,15 @@ typedef double mp_float_t; #define MP_UNLIKELY(x) __builtin_expect((x), 0) #endif +// To annotate that code is unreachable +#ifndef MP_UNREACHABLE +#if defined(__GNUC__) +#define MP_UNREACHABLE __builtin_unreachable(); +#else +#define MP_UNREACHABLE for (;;); +#endif +#endif + #ifndef MP_HTOBE16 #if MP_ENDIANNESS_LITTLE # define MP_HTOBE16(x) ((uint16_t)( (((x) & 0xff) << 8) | (((x) >> 8) & 0xff) )) @@ -1544,4 +1579,14 @@ typedef double mp_float_t; # define MP_WARN_CAT(x) (NULL) #endif +// Feature dependency check. +#if MICROPY_PY_SYS_SETTRACE +#if !MICROPY_PERSISTENT_CODE_SAVE +#error "MICROPY_PY_SYS_SETTRACE requires MICROPY_PERSISTENT_CODE_SAVE to be enabled" +#endif +#if MICROPY_COMP_CONST +#error "MICROPY_PY_SYS_SETTRACE requires MICROPY_COMP_CONST to be disabled" +#endif +#endif + #endif // MICROPY_INCLUDED_PY_MPCONFIG_H diff --git a/python/src/py/mphal.h b/python/src/py/mphal.h index 92de01d08..66d80705a 100644 --- a/python/src/py/mphal.h +++ b/python/src/py/mphal.h @@ -34,6 +34,10 @@ #include #endif +#ifndef mp_hal_stdio_poll +uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags); +#endif + #ifndef mp_hal_stdin_rx_chr int mp_hal_stdin_rx_chr(void); #endif diff --git a/python/src/py/mpstate.h b/python/src/py/mpstate.h index a9c2b32d6..ab7d8c51b 100644 --- a/python/src/py/mpstate.h +++ b/python/src/py/mpstate.h @@ -47,6 +47,7 @@ typedef struct mp_dynamic_compiler_t { bool opt_cache_map_lookup_in_bytecode; bool py_builtins_str_unicode; uint8_t native_arch; + uint8_t nlr_buf_num_regs; } mp_dynamic_compiler_t; extern mp_dynamic_compiler_t mp_dynamic_compiler; #endif @@ -141,7 +142,7 @@ typedef struct _mp_state_vm_t { volatile mp_obj_t mp_pending_exception; #if MICROPY_ENABLE_SCHEDULER - mp_sched_item_t sched_stack[MICROPY_SCHEDULER_DEPTH]; + mp_sched_item_t sched_queue[MICROPY_SCHEDULER_DEPTH]; #endif // current exception being handled, for sys.exc_info() @@ -149,6 +150,11 @@ typedef struct _mp_state_vm_t { mp_obj_base_t *cur_exception; #endif + #if MICROPY_PY_SYS_ATEXIT + // exposed through sys.atexit function + mp_obj_t sys_exitfunc; + #endif + // dictionary for the __main__ module mp_obj_dict_t dict_main; @@ -183,6 +189,10 @@ typedef struct _mp_state_vm_t { struct _mp_vfs_mount_t *vfs_mount_table; #endif + #if MICROPY_PY_BLUETOOTH + mp_obj_t bluetooth; + #endif + // // END ROOT POINTER SECTION //////////////////////////////////////////////////////////// @@ -200,6 +210,9 @@ typedef struct _mp_state_vm_t { #if MICROPY_ENABLE_COMPILER mp_uint_t mp_optimise_value; + #if MICROPY_EMIT_NATIVE + uint8_t default_emit_opt; // one of MP_EMIT_OPT_xxx + #endif #endif // size of the emergency exception buf, if it's dynamically allocated @@ -245,6 +258,12 @@ typedef struct _mp_state_thread_t { mp_obj_dict_t *dict_globals; nlr_buf_t *nlr_top; + + #if MICROPY_PY_SYS_SETTRACE + mp_obj_t prof_trace_callback; + bool prof_callback_is_executing; + struct _mp_code_state_t *current_code_state; + #endif } mp_state_thread_t; // This structure combines the above 3 structures. diff --git a/python/src/py/nativeglue.c b/python/src/py/nativeglue.c index c810f31e6..1a7f92f94 100644 --- a/python/src/py/nativeglue.c +++ b/python/src/py/nativeglue.c @@ -24,14 +24,15 @@ * THE SOFTWARE. */ +#include #include #include #include #include "py/runtime.h" #include "py/smallint.h" -#include "py/emitglue.h" -#include "py/bc.h" +#include "py/nativeglue.h" +#include "py/gc.h" #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_printf DEBUG_printf @@ -65,7 +66,7 @@ mp_uint_t mp_native_from_obj(mp_obj_t obj, mp_uint_t type) { case MP_NATIVE_TYPE_UINT: return mp_obj_get_int_truncated(obj); default: { // cast obj to a pointer mp_buffer_info_t bufinfo; - if (mp_get_buffer(obj, &bufinfo, MP_BUFFER_RW)) { + if (mp_get_buffer(obj, &bufinfo, MP_BUFFER_READ)) { return (mp_uint_t)bufinfo.buf; } else { // assume obj is an integer that represents an address @@ -77,7 +78,7 @@ mp_uint_t mp_native_from_obj(mp_obj_t obj, mp_uint_t type) { #endif -#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM +#if MICROPY_EMIT_MACHINE_CODE // convert a native value to a MicroPython object based on type mp_obj_t mp_native_to_obj(mp_uint_t val, mp_uint_t type) { @@ -95,9 +96,32 @@ mp_obj_t mp_native_to_obj(mp_uint_t val, mp_uint_t type) { #endif -#if MICROPY_EMIT_NATIVE +#if MICROPY_EMIT_NATIVE && !MICROPY_DYNAMIC_COMPILER -mp_obj_dict_t *mp_native_swap_globals(mp_obj_dict_t *new_globals) { +#if !MICROPY_PY_BUILTINS_SET +mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items) { + (void)n_args; + (void)items; + mp_raise_msg(&mp_type_RuntimeError, "set unsupported"); +} + +void mp_obj_set_store(mp_obj_t self_in, mp_obj_t item) { + (void)self_in; + (void)item; + mp_raise_msg(&mp_type_RuntimeError, "set unsupported"); +} +#endif + +#if !MICROPY_PY_BUILTINS_SLICE +mp_obj_t mp_obj_new_slice(mp_obj_t ostart, mp_obj_t ostop, mp_obj_t ostep) { + (void)ostart; + (void)ostop; + (void)ostep; + mp_raise_msg(&mp_type_RuntimeError, "slice unsupported"); +} +#endif + +STATIC mp_obj_dict_t *mp_native_swap_globals(mp_obj_dict_t *new_globals) { if (new_globals == NULL) { // Globals were the originally the same so don't restore them return NULL; @@ -113,13 +137,13 @@ mp_obj_dict_t *mp_native_swap_globals(mp_obj_dict_t *new_globals) { // wrapper that accepts n_args and n_kw in one argument // (native emitter can only pass at most 3 arguments to a function) -mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, size_t n_args_kw, const mp_obj_t *args) { +STATIC mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, size_t n_args_kw, const mp_obj_t *args) { return mp_call_function_n_kw(fun_in, n_args_kw & 0xff, (n_args_kw >> 8) & 0xff, args); } // wrapper that makes raise obj and raises it // END_FINALLY opcode requires that we don't raise if o==None -void mp_native_raise(mp_obj_t o) { +STATIC void mp_native_raise(mp_obj_t o) { if (o != MP_OBJ_NULL && o != mp_const_none) { nlr_raise(mp_make_raise_obj(o)); } @@ -187,8 +211,50 @@ STATIC bool mp_native_yield_from(mp_obj_t gen, mp_obj_t send_value, mp_obj_t *re return false; } +#if MICROPY_PY_BUILTINS_FLOAT + +STATIC mp_obj_t mp_obj_new_float_from_f(float f) { + return mp_obj_new_float((mp_float_t)f); +} + +STATIC mp_obj_t mp_obj_new_float_from_d(double d) { + return mp_obj_new_float((mp_float_t)d); +} + +STATIC float mp_obj_get_float_to_f(mp_obj_t o) { + return (float)mp_obj_get_float(o); +} + +STATIC double mp_obj_get_float_to_d(mp_obj_t o) { + return (double)mp_obj_get_float(o); +} + +#else + +STATIC mp_obj_t mp_obj_new_float_from_f(float f) { + (void)f; + mp_raise_msg(&mp_type_RuntimeError, "float unsupported"); +} + +STATIC mp_obj_t mp_obj_new_float_from_d(double d) { + (void)d; + mp_raise_msg(&mp_type_RuntimeError, "float unsupported"); +} + +STATIC float mp_obj_get_float_to_f(mp_obj_t o) { + (void)o; + mp_raise_msg(&mp_type_RuntimeError, "float unsupported"); +} + +STATIC double mp_obj_get_float_to_d(mp_obj_t o) { + (void)o; + mp_raise_msg(&mp_type_RuntimeError, "float unsupported"); +} + +#endif + // these must correspond to the respective enum in runtime0.h -const void *const mp_fun_table[MP_F_NUMBER_OF] = { +const mp_fun_table_t mp_fun_table = { &mp_const_none_obj, &mp_const_false_obj, &mp_const_true_obj, @@ -210,45 +276,74 @@ const void *const mp_fun_table[MP_F_NUMBER_OF] = { mp_binary_op, mp_obj_new_tuple, mp_obj_new_list, - mp_obj_list_append, mp_obj_new_dict, - mp_obj_dict_store, -#if MICROPY_PY_BUILTINS_SET - mp_obj_set_store, mp_obj_new_set, -#endif + mp_obj_set_store, + mp_obj_list_append, + mp_obj_dict_store, mp_make_function_from_raw_code, mp_native_call_function_n_kw, mp_call_method_n_kw, mp_call_method_n_kw_var, mp_native_getiter, mp_native_iternext, + #if MICROPY_NLR_SETJMP + nlr_push_tail, + #else nlr_push, + #endif nlr_pop, mp_native_raise, mp_import_name, mp_import_from, mp_import_all, -#if MICROPY_PY_BUILTINS_SLICE mp_obj_new_slice, -#endif mp_unpack_sequence, mp_unpack_ex, mp_delete_name, mp_delete_global, - mp_obj_new_cell, mp_make_closure_from_raw_code, mp_arg_check_num_sig, mp_setup_code_state, mp_small_int_floor_divide, mp_small_int_modulo, mp_native_yield_from, + #if MICROPY_NLR_SETJMP + setjmp, + #else + NULL, + #endif + // Additional entries for dynamic runtime, starts at index 50 + memset, + memmove, + gc_realloc, + mp_printf, + mp_vprintf, + mp_raise_msg, + mp_obj_get_type, + mp_obj_new_str, + mp_obj_new_bytes, + mp_obj_new_bytearray_by_ref, + mp_obj_new_float_from_f, + mp_obj_new_float_from_d, + mp_obj_get_float_to_f, + mp_obj_get_float_to_d, + mp_get_buffer_raise, + mp_get_stream_raise, + &mp_plat_print, + &mp_type_type, + &mp_type_str, + &mp_type_list, + &mp_type_dict, + &mp_type_fun_builtin_0, + &mp_type_fun_builtin_1, + &mp_type_fun_builtin_2, + &mp_type_fun_builtin_3, + &mp_type_fun_builtin_var, + &mp_stream_read_obj, + &mp_stream_readinto_obj, + &mp_stream_unbuffered_readline_obj, + &mp_stream_write_obj, }; -/* -void mp_f_vector(mp_fun_kind_t fun_kind) { - (mp_f_table[fun_kind])(); -} -*/ - #endif // MICROPY_EMIT_NATIVE diff --git a/python/src/py/nativeglue.h b/python/src/py/nativeglue.h new file mode 100644 index 000000000..021e7a8ec --- /dev/null +++ b/python/src/py/nativeglue.h @@ -0,0 +1,177 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT HOLDERS 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. + */ +#ifndef MICROPY_INCLUDED_PY_NATIVEGLUE_H +#define MICROPY_INCLUDED_PY_NATIVEGLUE_H + +#include +#include "py/obj.h" +#include "py/persistentcode.h" +#include "py/stream.h" + +typedef enum { + MP_F_CONST_NONE_OBJ = 0, + MP_F_CONST_FALSE_OBJ, + MP_F_CONST_TRUE_OBJ, + MP_F_CONVERT_OBJ_TO_NATIVE, + MP_F_CONVERT_NATIVE_TO_OBJ, + MP_F_NATIVE_SWAP_GLOBALS, + MP_F_LOAD_NAME, + MP_F_LOAD_GLOBAL, + MP_F_LOAD_BUILD_CLASS, + MP_F_LOAD_ATTR, + MP_F_LOAD_METHOD, + MP_F_LOAD_SUPER_METHOD, + MP_F_STORE_NAME, + MP_F_STORE_GLOBAL, + MP_F_STORE_ATTR, + MP_F_OBJ_SUBSCR, + MP_F_OBJ_IS_TRUE, + MP_F_UNARY_OP, + MP_F_BINARY_OP, + MP_F_BUILD_TUPLE, + MP_F_BUILD_LIST, + MP_F_BUILD_MAP, + MP_F_BUILD_SET, + MP_F_STORE_SET, + MP_F_LIST_APPEND, + MP_F_STORE_MAP, + MP_F_MAKE_FUNCTION_FROM_RAW_CODE, + MP_F_NATIVE_CALL_FUNCTION_N_KW, + MP_F_CALL_METHOD_N_KW, + MP_F_CALL_METHOD_N_KW_VAR, + MP_F_NATIVE_GETITER, + MP_F_NATIVE_ITERNEXT, + MP_F_NLR_PUSH, + MP_F_NLR_POP, + MP_F_NATIVE_RAISE, + MP_F_IMPORT_NAME, + MP_F_IMPORT_FROM, + MP_F_IMPORT_ALL, + MP_F_NEW_SLICE, + MP_F_UNPACK_SEQUENCE, + MP_F_UNPACK_EX, + MP_F_DELETE_NAME, + MP_F_DELETE_GLOBAL, + MP_F_MAKE_CLOSURE_FROM_RAW_CODE, + MP_F_ARG_CHECK_NUM_SIG, + MP_F_SETUP_CODE_STATE, + MP_F_SMALL_INT_FLOOR_DIVIDE, + MP_F_SMALL_INT_MODULO, + MP_F_NATIVE_YIELD_FROM, + MP_F_SETJMP, + MP_F_NUMBER_OF, +} mp_fun_kind_t; + +typedef struct _mp_fun_table_t { + mp_const_obj_t const_none; + mp_const_obj_t const_false; + mp_const_obj_t const_true; + mp_uint_t (*native_from_obj)(mp_obj_t obj, mp_uint_t type); + mp_obj_t (*native_to_obj)(mp_uint_t val, mp_uint_t type); + mp_obj_dict_t *(*swap_globals)(mp_obj_dict_t *new_globals); + mp_obj_t (*load_name)(qstr qst); + mp_obj_t (*load_global)(qstr qst); + mp_obj_t (*load_build_class)(void); + mp_obj_t (*load_attr)(mp_obj_t base, qstr attr); + void (*load_method)(mp_obj_t base, qstr attr, mp_obj_t *dest); + void (*load_super_method)(qstr attr, mp_obj_t *dest); + void (*store_name)(qstr qst, mp_obj_t obj); + void (*store_global)(qstr qst, mp_obj_t obj); + void (*store_attr)(mp_obj_t base, qstr attr, mp_obj_t val); + mp_obj_t (*obj_subscr)(mp_obj_t base, mp_obj_t index, mp_obj_t val); + bool (*obj_is_true)(mp_obj_t arg); + mp_obj_t (*unary_op)(mp_unary_op_t op, mp_obj_t arg); + mp_obj_t (*binary_op)(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs); + mp_obj_t (*new_tuple)(size_t n, const mp_obj_t *items); + mp_obj_t (*new_list)(size_t n, mp_obj_t *items); + mp_obj_t (*new_dict)(size_t n_args); + mp_obj_t (*new_set)(size_t n_args, mp_obj_t *items); + void (*set_store)(mp_obj_t self_in, mp_obj_t item); + mp_obj_t (*list_append)(mp_obj_t self_in, mp_obj_t arg); + mp_obj_t (*dict_store)(mp_obj_t self_in, mp_obj_t key, mp_obj_t value); + mp_obj_t (*make_function_from_raw_code)(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args); + mp_obj_t (*call_function_n_kw)(mp_obj_t fun_in, size_t n_args_kw, const mp_obj_t *args); + mp_obj_t (*call_method_n_kw)(size_t n_args, size_t n_kw, const mp_obj_t *args); + mp_obj_t (*call_method_n_kw_var)(bool have_self, size_t n_args_n_kw, const mp_obj_t *args); + mp_obj_t (*getiter)(mp_obj_t obj, mp_obj_iter_buf_t *iter); + mp_obj_t (*iternext)(mp_obj_iter_buf_t *iter); + unsigned int (*nlr_push)(nlr_buf_t *); + void (*nlr_pop)(void); + void (*raise)(mp_obj_t o); + mp_obj_t (*import_name)(qstr name, mp_obj_t fromlist, mp_obj_t level); + mp_obj_t (*import_from)(mp_obj_t module, qstr name); + void (*import_all)(mp_obj_t module); + mp_obj_t (*new_slice)(mp_obj_t start, mp_obj_t stop, mp_obj_t step); + void (*unpack_sequence)(mp_obj_t seq, size_t num, mp_obj_t *items); + void (*unpack_ex)(mp_obj_t seq, size_t num, mp_obj_t *items); + void (*delete_name)(qstr qst); + void (*delete_global)(qstr qst); + mp_obj_t (*make_closure_from_raw_code)(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args); + void (*arg_check_num_sig)(size_t n_args, size_t n_kw, uint32_t sig); + void (*setup_code_state)(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args); + mp_int_t (*small_int_floor_divide)(mp_int_t num, mp_int_t denom); + mp_int_t (*small_int_modulo)(mp_int_t dividend, mp_int_t divisor); + bool (*yield_from)(mp_obj_t gen, mp_obj_t send_value, mp_obj_t *ret_value); + void *setjmp; + // Additional entries for dynamic runtime, starts at index 50 + void *(*memset_)(void *s, int c, size_t n); + void *(*memmove_)(void *dest, const void *src, size_t n); + void *(*realloc_)(void *ptr, size_t n_bytes, bool allow_move); + int (*printf_)(const mp_print_t *print, const char *fmt, ...); + int (*vprintf_)(const mp_print_t *print, const char *fmt, va_list args); + #if defined(__GNUC__) + NORETURN // Only certain compilers support no-return attributes in function pointer declarations + #endif + void (*raise_msg)(const mp_obj_type_t *exc_type, const char *msg); + mp_obj_type_t *(*obj_get_type)(mp_const_obj_t o_in); + mp_obj_t (*obj_new_str)(const char* data, size_t len); + mp_obj_t (*obj_new_bytes)(const byte* data, size_t len); + mp_obj_t (*obj_new_bytearray_by_ref)(size_t n, void *items); + mp_obj_t (*obj_new_float_from_f)(float f); + mp_obj_t (*obj_new_float_from_d)(double d); + float (*obj_get_float_to_f)(mp_obj_t o); + double (*obj_get_float_to_d)(mp_obj_t o); + void (*get_buffer_raise)(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags); + const mp_stream_p_t *(*get_stream_raise)(mp_obj_t self_in, int flags); + const mp_print_t *plat_print; + const mp_obj_type_t *type_type; + const mp_obj_type_t *type_str; + const mp_obj_type_t *type_list; + const mp_obj_type_t *type_dict; + const mp_obj_type_t *type_fun_builtin_0; + const mp_obj_type_t *type_fun_builtin_1; + const mp_obj_type_t *type_fun_builtin_2; + const mp_obj_type_t *type_fun_builtin_3; + const mp_obj_type_t *type_fun_builtin_var; + const mp_obj_fun_builtin_var_t *stream_read_obj; + const mp_obj_fun_builtin_var_t *stream_readinto_obj; + const mp_obj_fun_builtin_var_t *stream_unbuffered_readline_obj; + const mp_obj_fun_builtin_var_t *stream_write_obj; +} mp_fun_table_t; + +extern const mp_fun_table_t mp_fun_table; + +#endif // MICROPY_INCLUDED_PY_NATIVEGLUE_H diff --git a/python/src/py/nlr.h b/python/src/py/nlr.h index 90595a12d..3be3eb58c 100644 --- a/python/src/py/nlr.h +++ b/python/src/py/nlr.h @@ -34,6 +34,14 @@ #include "py/mpconfig.h" +#define MICROPY_NLR_NUM_REGS_X86 (6) +#define MICROPY_NLR_NUM_REGS_X64 (8) +#define MICROPY_NLR_NUM_REGS_X64_WIN (10) +#define MICROPY_NLR_NUM_REGS_ARM_THUMB (10) +#define MICROPY_NLR_NUM_REGS_ARM_THUMB_FP (10 + 6) +#define MICROPY_NLR_NUM_REGS_XTENSA (10) +#define MICROPY_NLR_NUM_REGS_XTENSAWIN (17) + // If MICROPY_NLR_SETJMP is not enabled then auto-detect the machine arch #if !MICROPY_NLR_SETJMP // A lot of nlr-related things need different treatment on Windows @@ -44,20 +52,31 @@ #endif #if defined(__i386__) #define MICROPY_NLR_X86 (1) - #define MICROPY_NLR_NUM_REGS (6) + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_X86) #elif defined(__x86_64__) #define MICROPY_NLR_X64 (1) #if MICROPY_NLR_OS_WINDOWS - #define MICROPY_NLR_NUM_REGS (10) + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_X64_WIN) #else - #define MICROPY_NLR_NUM_REGS (8) + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_X64) #endif #elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) #define MICROPY_NLR_THUMB (1) - #define MICROPY_NLR_NUM_REGS (10) + #if defined(__SOFTFP__) + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_ARM_THUMB) + #else + // With hardware FP registers s16-s31 are callee save so in principle + // should be saved and restored by the NLR code. gcc only uses s16-s21 + // so only save/restore those as an optimisation. + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_ARM_THUMB_FP) + #endif #elif defined(__xtensa__) #define MICROPY_NLR_XTENSA (1) - #define MICROPY_NLR_NUM_REGS (10) + #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_XTENSA) +#elif defined(__powerpc__) + #define MICROPY_NLR_POWERPC (1) + // this could be less but using 128 for safety + #define MICROPY_NLR_NUM_REGS (128) #else #define MICROPY_NLR_SETJMP (1) //#warning "No native NLR support for this arch, using setjmp implementation" diff --git a/python/src/py/nlrpowerpc.c b/python/src/py/nlrpowerpc.c new file mode 100644 index 000000000..b40382381 --- /dev/null +++ b/python/src/py/nlrpowerpc.c @@ -0,0 +1,121 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019, Michael Neuling, IBM Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT HOLDERS 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 "py/mpstate.h" + +#if MICROPY_NLR_POWERPC + +#undef nlr_push + +// Saving all ABI non-vol registers here + +unsigned int nlr_push(nlr_buf_t *nlr) { + + __asm__ volatile( + "li 4, 0x4eed ; " // Store canary + "std 4, 0x00(%0) ;" + "std 0, 0x08(%0) ;" + "std 1, 0x10(%0) ;" + "std 2, 0x18(%0) ;" + "std 14, 0x20(%0) ;" + "std 15, 0x28(%0) ;" + "std 16, 0x30(%0) ;" + "std 17, 0x38(%0) ;" + "std 18, 0x40(%0) ;" + "std 19, 0x48(%0) ;" + "std 20, 0x50(%0) ;" + "std 21, 0x58(%0) ;" + "std 22, 0x60(%0) ;" + "std 23, 0x68(%0) ;" + "std 24, 0x70(%0) ;" + "std 25, 0x78(%0) ;" + "std 26, 0x80(%0) ;" + "std 27, 0x88(%0) ;" + "std 28, 0x90(%0) ;" + "std 29, 0x98(%0) ;" + "std 30, 0xA0(%0) ;" + "std 31, 0xA8(%0) ;" + + "mfcr 4 ; " + "std 4, 0xB0(%0) ;" + "mflr 4 ;" + "std 4, 0xB8(%0) ;" + "li 4, nlr_push_tail@l ;" + "oris 4, 4, nlr_push_tail@h ;" + "mtctr 4 ;" + "mr 3, %1 ; " + "bctr ;" + : + : "r"(&nlr->regs), "r"(nlr) + : + ); + + return 0; +} + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + + __asm__ volatile( + "ld 3, 0x0(%0) ;" + "cmpdi 3, 0x4eed ; " // Check canary + "bne . ; " + "ld 0, 0x08(%0) ;" + "ld 1, 0x10(%0) ;" + "ld 2, 0x18(%0) ;" + "ld 14, 0x20(%0) ;" + "ld 15, 0x28(%0) ;" + "ld 16, 0x30(%0) ;" + "ld 17, 0x38(%0) ;" + "ld 18, 0x40(%0) ;" + "ld 19, 0x48(%0) ;" + "ld 20, 0x50(%0) ;" + "ld 21, 0x58(%0) ;" + "ld 22, 0x60(%0) ;" + "ld 23, 0x68(%0) ;" + "ld 24, 0x70(%0) ;" + "ld 25, 0x78(%0) ;" + "ld 26, 0x80(%0) ;" + "ld 27, 0x88(%0) ;" + "ld 28, 0x90(%0) ;" + "ld 29, 0x98(%0) ;" + "ld 30, 0xA0(%0) ;" + "ld 31, 0xA8(%0) ;" + "ld 3, 0xB0(%0) ;" + "mtcr 3 ;" + "ld 3, 0xB8(%0) ;" + "mtlr 3 ; " + "li 3, 1;" + "blr ;" + : + : "r"(&top->regs) + : + ); + + MP_UNREACHABLE; +} + +#endif // MICROPY_NLR_POWERPC diff --git a/python/src/py/nlrthumb.c b/python/src/py/nlrthumb.c index 99061e62c..bc3038827 100644 --- a/python/src/py/nlrthumb.c +++ b/python/src/py/nlrthumb.c @@ -44,7 +44,7 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { "str r6, [r0, #20] \n" // store r6 into nlr_buf "str r7, [r0, #24] \n" // store r7 into nlr_buf -#if defined(__ARM_ARCH_6M__) +#if !defined(__thumb2__) "mov r1, r8 \n" "str r1, [r0, #28] \n" // store r8 into nlr_buf "mov r1, r9 \n" @@ -63,10 +63,15 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { "str r10, [r0, #36] \n" // store r10 into nlr_buf "str r11, [r0, #40] \n" // store r11 into nlr_buf "str r13, [r0, #44] \n" // store r13=sp into nlr_buf + #if MICROPY_NLR_NUM_REGS == 16 + "vstr d8, [r0, #48] \n" // store s16-s17 into nlr_buf + "vstr d9, [r0, #56] \n" // store s18-s19 into nlr_buf + "vstr d10, [r0, #64] \n" // store s20-s21 into nlr_buf + #endif "str lr, [r0, #8] \n" // store lr into nlr_buf #endif -#if defined(__ARM_ARCH_6M__) +#if !defined(__thumb2__) "ldr r1, nlr_push_tail_var \n" "bx r1 \n" // do the rest in C ".align 2 \n" @@ -97,7 +102,7 @@ NORETURN void nlr_jump(void *val) { "ldr r6, [r0, #20] \n" // load r6 from nlr_buf "ldr r7, [r0, #24] \n" // load r7 from nlr_buf -#if defined(__ARM_ARCH_6M__) +#if !defined(__thumb2__) "ldr r1, [r0, #28] \n" // load r8 from nlr_buf "mov r8, r1 \n" "ldr r1, [r0, #32] \n" // load r9 from nlr_buf @@ -116,6 +121,11 @@ NORETURN void nlr_jump(void *val) { "ldr r10, [r0, #36] \n" // load r10 from nlr_buf "ldr r11, [r0, #40] \n" // load r11 from nlr_buf "ldr r13, [r0, #44] \n" // load r13=sp from nlr_buf + #if MICROPY_NLR_NUM_REGS == 16 + "vldr d8, [r0, #48] \n" // load s16-s17 from nlr_buf + "vldr d9, [r0, #56] \n" // load s18-s19 from nlr_buf + "vldr d10, [r0, #64] \n" // load s20-s21 from nlr_buf + #endif "ldr lr, [r0, #8] \n" // load lr from nlr_buf #endif "movs r0, #1 \n" // return 1, non-local return @@ -125,11 +135,7 @@ NORETURN void nlr_jump(void *val) { : // clobbered registers ); - #if defined(__GNUC__) - __builtin_unreachable(); - #else - for (;;); // needed to silence compiler warning - #endif + MP_UNREACHABLE } #endif // MICROPY_NLR_THUMB diff --git a/python/src/py/nlrx64.c b/python/src/py/nlrx64.c index a3a1cf341..95496b380 100644 --- a/python/src/py/nlrx64.c +++ b/python/src/py/nlrx64.c @@ -108,7 +108,7 @@ NORETURN void nlr_jump(void *val) { : // clobbered registers ); - for (;;); // needed to silence compiler warning + MP_UNREACHABLE } #endif // MICROPY_NLR_X64 diff --git a/python/src/py/nlrx86.c b/python/src/py/nlrx86.c index 59b97d8ee..6195db63c 100644 --- a/python/src/py/nlrx86.c +++ b/python/src/py/nlrx86.c @@ -100,7 +100,7 @@ NORETURN void nlr_jump(void *val) { : // clobbered registers ); - for (;;); // needed to silence compiler warning + MP_UNREACHABLE } #endif // MICROPY_NLR_X86 diff --git a/python/src/py/nlrxtensa.c b/python/src/py/nlrxtensa.c index cd3dee364..895b2029e 100644 --- a/python/src/py/nlrxtensa.c +++ b/python/src/py/nlrxtensa.c @@ -77,7 +77,7 @@ NORETURN void nlr_jump(void *val) { : // clobbered registers ); - for (;;); // needed to silence compiler warning + MP_UNREACHABLE } #endif // MICROPY_NLR_XTENSA diff --git a/python/src/py/obj.c b/python/src/py/obj.c index 122f0ea62..4588d896a 100644 --- a/python/src/py/obj.c +++ b/python/src/py/obj.c @@ -93,7 +93,7 @@ void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc) { #endif // the block name can be NULL if it's unknown qstr block = values[i + 2]; - if (block == MP_QSTR_NULL) { + if (block == MP_QSTRnull) { mp_print_str(print, "\n"); } else { mp_printf(print, ", in %q\n", block); @@ -113,7 +113,7 @@ bool mp_obj_is_true(mp_obj_t arg) { } else if (arg == mp_const_none) { return 0; } else if (mp_obj_is_small_int(arg)) { - if (MP_OBJ_SMALL_INT_VALUE(arg) == 0) { + if (arg == MP_OBJ_NEW_SMALL_INT(0)) { return 0; } else { return 1; diff --git a/python/src/py/obj.h b/python/src/py/obj.h index a22d5f8b8..5b54892ce 100644 --- a/python/src/py/obj.h +++ b/python/src/py/obj.h @@ -691,7 +691,6 @@ mp_float_t mp_obj_get_float(mp_obj_t self_in); bool mp_obj_get_float_maybe(mp_obj_t arg, mp_float_t *value); void mp_obj_get_complex(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag); #endif -//qstr mp_obj_get_qstr(mp_obj_t arg); void mp_obj_get_array(mp_obj_t o, size_t *len, mp_obj_t **items); // *items may point inside a GC block void mp_obj_get_array_fixed_n(mp_obj_t o, size_t len, mp_obj_t **items); // *items may point inside a GC block size_t mp_get_index(const mp_obj_type_t *type, size_t len, mp_obj_t index, bool is_slice); diff --git a/python/src/py/objarray.c b/python/src/py/objarray.c index 4e58d8e5d..c19617d4e 100644 --- a/python/src/py/objarray.c +++ b/python/src/py/objarray.c @@ -445,7 +445,7 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value if (len_adj > o->free) { // TODO: alloc policy; at the moment we go conservative o->items = m_renew(byte, o->items, (o->len + o->free) * item_sz, (o->len + len_adj) * item_sz); - o->free = 0; + o->free = len_adj; dest_items = o->items; } mp_seq_replace_slice_grow_inplace(dest_items, o->len, @@ -458,6 +458,7 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value mp_seq_clear(dest_items, o->len + len_adj, o->len, item_sz); // TODO: alloc policy after shrinking } + o->free -= len_adj; o->len += len_adj; return mp_const_none; #else diff --git a/python/src/py/objdict.c b/python/src/py/objdict.c index 0a223f731..7a43a8548 100644 --- a/python/src/py/objdict.c +++ b/python/src/py/objdict.c @@ -31,6 +31,7 @@ #include "py/runtime.h" #include "py/builtin.h" #include "py/objtype.h" +#include "py/objstr.h" #define mp_obj_is_dict_type(o) (mp_obj_is_obj(o) && ((mp_obj_base_t*)MP_OBJ_TO_PTR(o))->type->make_new == dict_make_new) @@ -59,7 +60,7 @@ STATIC void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ if (!(MICROPY_PY_UJSON && kind == PRINT_JSON)) { kind = PRINT_REPR; } - if (MICROPY_PY_COLLECTIONS_ORDEREDDICT && self->base.type != &mp_type_dict) { + if (MICROPY_PY_COLLECTIONS_ORDEREDDICT && self->base.type != &mp_type_dict && kind != PRINT_JSON) { mp_printf(print, "%q(", self->base.type->name); } mp_print_str(print, "{"); @@ -70,12 +71,19 @@ STATIC void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ mp_print_str(print, ", "); } first = false; + bool add_quote = MICROPY_PY_UJSON && kind == PRINT_JSON && !mp_obj_is_str_or_bytes(next->key); + if (add_quote) { + mp_print_str(print, "\""); + } mp_obj_print_helper(print, next->key, kind); + if (add_quote) { + mp_print_str(print, "\""); + } mp_print_str(print, ": "); mp_obj_print_helper(print, next->value, kind); } mp_print_str(print, "}"); - if (MICROPY_PY_COLLECTIONS_ORDEREDDICT && self->base.type != &mp_type_dict) { + if (MICROPY_PY_COLLECTIONS_ORDEREDDICT && self->base.type != &mp_type_dict && kind != PRINT_JSON) { mp_print_str(print, ")"); } } diff --git a/python/src/py/objenumerate.c b/python/src/py/objenumerate.c index 493e45c2a..243c9f83a 100644 --- a/python/src/py/objenumerate.c +++ b/python/src/py/objenumerate.c @@ -59,7 +59,7 @@ STATIC mp_obj_t enumerate_make_new(const mp_obj_type_t *type, size_t n_args, siz o->iter = mp_getiter(arg_vals.iterable.u_obj, NULL); o->cur = arg_vals.start.u_int; #else - (void)n_kw; + mp_arg_check_num(n_args, n_kw, 1, 2, false); mp_obj_enumerate_t *o = m_new_obj(mp_obj_enumerate_t); o->base.type = type; o->iter = mp_getiter(args[0], NULL); diff --git a/python/src/py/objexcept.c b/python/src/py/objexcept.c index 1fb636f66..dadbe98ae 100644 --- a/python/src/py/objexcept.c +++ b/python/src/py/objexcept.c @@ -100,11 +100,6 @@ mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in) { #endif #endif // MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF -// Instance of GeneratorExit exception - needed by generator.close() -// This would belong to objgenerator.c, but to keep mp_obj_exception_t -// definition module-private so far, have it here. -const mp_obj_exception_t mp_const_GeneratorExit_obj = {{&mp_type_GeneratorExit}, 0, 0, NULL, (mp_obj_tuple_t*)&mp_const_empty_tuple_obj}; - void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { mp_obj_exception_t *o = MP_OBJ_TO_PTR(o_in); mp_print_kind_t k = kind & ~PRINT_EXC_SUBCLASS; @@ -126,7 +121,7 @@ void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kin // try to provide a nice OSError error message if (o->base.type == &mp_type_OSError && mp_obj_is_small_int(o->args->items[0])) { qstr qst = mp_errno_to_str(o->args->items[0]); - if (qst != MP_QSTR_NULL) { + if (qst != MP_QSTRnull) { mp_printf(print, "[Errno " INT_FMT "] %q", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), qst); return; } diff --git a/python/src/py/objfun.c b/python/src/py/objfun.c index d96c79ede..7051f3476 100644 --- a/python/src/py/objfun.c +++ b/python/src/py/objfun.c @@ -139,7 +139,7 @@ const mp_obj_type_t mp_type_fun_builtin_var = { /* byte code functions */ qstr mp_obj_code_get_name(const byte *code_info) { - code_info = mp_decode_uint_skip(code_info); // skip code_info_size entry + MP_BC_PRELUDE_SIZE_DECODE(code_info); #if MICROPY_PERSISTENT_CODE return code_info[0] | (code_info[1] << 8); #else @@ -161,12 +161,7 @@ qstr mp_obj_fun_get_name(mp_const_obj_t fun_in) { #endif const byte *bc = fun->bytecode; - bc = mp_decode_uint_skip(bc); // skip n_state - bc = mp_decode_uint_skip(bc); // skip n_exc_stack - bc++; // skip scope_params - bc++; // skip n_pos_args - bc++; // skip n_kwonly_args - bc++; // skip n_def_pos_args + MP_BC_PRELUDE_SIG_DECODE(bc); return mp_obj_code_get_name(bc); } @@ -197,18 +192,19 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) { #define DECODE_CODESTATE_SIZE(bytecode, n_state_out_var, state_size_out_var) \ { \ - /* bytecode prelude: state size and exception stack size */ \ - n_state_out_var = mp_decode_uint_value(bytecode); \ - size_t n_exc_stack = mp_decode_uint_value(mp_decode_uint_skip(bytecode)); \ - \ + const uint8_t *ip = bytecode; \ + size_t n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_args; \ + MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state_out_var, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_args); \ + \ /* state size in bytes */ \ state_size_out_var = n_state_out_var * sizeof(mp_obj_t) \ + n_exc_stack * sizeof(mp_exc_stack_t); \ } -#define INIT_CODESTATE(code_state, _fun_bc, n_args, n_kw, args) \ +#define INIT_CODESTATE(code_state, _fun_bc, _n_state, n_args, n_kw, args) \ code_state->fun_bc = _fun_bc; \ code_state->ip = 0; \ + code_state->n_state = _n_state; \ mp_setup_code_state(code_state, n_args, n_kw, args); \ code_state->old_globals = mp_globals_get(); @@ -235,7 +231,7 @@ mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args } #endif - INIT_CODESTATE(code_state, self, n_args, n_kw, args); + INIT_CODESTATE(code_state, self, n_state, n_args, n_kw, args); // execute the byte code with the correct globals context mp_globals_set(self->globals); @@ -280,7 +276,7 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const } #endif - INIT_CODESTATE(code_state, self, n_args, n_kw, args); + INIT_CODESTATE(code_state, self, n_state, n_args, n_kw, args); // execute the byte code with the correct globals context mp_globals_set(self->globals); @@ -294,9 +290,11 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const assert(0); } } - const byte *bytecode_ptr = mp_decode_uint_skip(mp_decode_uint_skip(self->bytecode)); - size_t n_pos_args = bytecode_ptr[1]; - size_t n_kwonly_args = bytecode_ptr[2]; + const byte *bytecode_ptr = self->bytecode; + size_t n_state_unused, n_exc_stack_unused, scope_flags_unused; + size_t n_pos_args, n_kwonly_args, n_def_args_unused; + MP_BC_PRELUDE_SIG_DECODE_INTO(bytecode_ptr, n_state_unused, n_exc_stack_unused, + scope_flags_unused, n_pos_args, n_kwonly_args, n_def_args_unused); // We can't check the case when an exception is returned in state[0] // and there are no arguments, because in this case our detection slot may have // been overwritten by the returned exception (which is allowed). @@ -472,7 +470,7 @@ STATIC mp_uint_t convert_obj_for_inline_asm(mp_obj_t obj) { return (mp_uint_t)items; } else { mp_buffer_info_t bufinfo; - if (mp_get_buffer(obj, &bufinfo, MP_BUFFER_WRITE)) { + if (mp_get_buffer(obj, &bufinfo, MP_BUFFER_READ)) { // supports the buffer protocol, return a pointer to the data return (mp_uint_t)bufinfo.buf; } else { diff --git a/python/src/py/objfun.h b/python/src/py/objfun.h index 257b8a65a..905b5dbca 100644 --- a/python/src/py/objfun.h +++ b/python/src/py/objfun.h @@ -33,6 +33,9 @@ typedef struct _mp_obj_fun_bc_t { mp_obj_dict_t *globals; // the context within which this function was defined const byte *bytecode; // bytecode for the function const mp_uint_t *const_table; // constant table + #if MICROPY_PY_SYS_SETTRACE + const struct _mp_raw_code_t *rc; + #endif // the following extra_args array is allocated space to take (in order): // - values of positional default args (if any) // - a single slot for default kw args dict (if it has them) diff --git a/python/src/py/objgenerator.c b/python/src/py/objgenerator.c index 29c7cb16d..903a6469c 100644 --- a/python/src/py/objgenerator.c +++ b/python/src/py/objgenerator.c @@ -30,16 +30,23 @@ #include "py/runtime.h" #include "py/bc.h" +#include "py/objstr.h" #include "py/objgenerator.h" #include "py/objfun.h" #include "py/stackctrl.h" +// Instance of GeneratorExit exception - needed by generator.close() +const mp_obj_exception_t mp_const_GeneratorExit_obj = {{&mp_type_GeneratorExit}, 0, 0, NULL, (mp_obj_tuple_t*)&mp_const_empty_tuple_obj}; + /******************************************************************************/ /* generator wrapper */ typedef struct _mp_obj_gen_instance_t { mp_obj_base_t base; - mp_obj_dict_t *globals; + // mp_const_none: Not-running, no exception. + // MP_OBJ_NULL: Running, no exception. + // other: Not running, pending exception. + mp_obj_t pend_exc; mp_code_state_t code_state; } mp_obj_gen_instance_t; @@ -48,17 +55,18 @@ STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, cons mp_obj_fun_bc_t *self_fun = MP_OBJ_TO_PTR(self_in); // bytecode prelude: get state size and exception stack size - size_t n_state = mp_decode_uint_value(self_fun->bytecode); - size_t n_exc_stack = mp_decode_uint_value(mp_decode_uint_skip(self_fun->bytecode)); + const uint8_t *ip = self_fun->bytecode; + MP_BC_PRELUDE_SIG_DECODE(ip); // allocate the generator object, with room for local stack and exception stack mp_obj_gen_instance_t *o = m_new_obj_var(mp_obj_gen_instance_t, byte, n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t)); o->base.type = &mp_type_gen_instance; - o->globals = self_fun->globals; + o->pend_exc = mp_const_none; o->code_state.fun_bc = self_fun; o->code_state.ip = 0; + o->code_state.n_state = n_state; mp_setup_code_state(&o->code_state, n_args, n_kw, args); return MP_OBJ_FROM_PTR(o); } @@ -84,7 +92,14 @@ STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_k // Determine start of prelude, and extract n_state from it uintptr_t prelude_offset = ((uintptr_t*)self_fun->bytecode)[0]; - size_t n_state = mp_decode_uint_value(self_fun->bytecode + prelude_offset); + #if MICROPY_EMIT_NATIVE_PRELUDE_AS_BYTES_OBJ + // Prelude is in bytes object in const_table, at index prelude_offset + mp_obj_str_t *prelude_bytes = MP_OBJ_TO_PTR(self_fun->const_table[prelude_offset]); + prelude_offset = (const byte*)prelude_bytes->data - self_fun->bytecode; + #endif + const uint8_t *ip = self_fun->bytecode + prelude_offset; + size_t n_state, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_args; + MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_args); size_t n_exc_stack = 0; // Allocate the generator object, with room for local stack and exception stack @@ -93,13 +108,14 @@ STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_k o->base.type = &mp_type_gen_instance; // Parse the input arguments and set up the code state - o->globals = self_fun->globals; + o->pend_exc = mp_const_none; o->code_state.fun_bc = self_fun; o->code_state.ip = (const byte*)prelude_offset; + o->code_state.n_state = n_state; mp_setup_code_state(&o->code_state, n_args, n_kw, args); // Indicate we are a native function, which doesn't use this variable - o->code_state.exc_sp = NULL; + o->code_state.exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_SENTINEL; // Prepare the generator instance for execution uintptr_t start_offset = ((uintptr_t*)self_fun->bytecode)[1]; @@ -138,38 +154,39 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ *ret_val = MP_OBJ_STOP_ITERATION; return MP_VM_RETURN_NORMAL; } + + // Ensure the generator cannot be reentered during execution + if (self->pend_exc == MP_OBJ_NULL) { + mp_raise_ValueError("generator already executing"); + } + + #if MICROPY_PY_GENERATOR_PEND_THROW + // If exception is pending (set using .pend_throw()), process it now. + if (self->pend_exc != mp_const_none) { + throw_value = self->pend_exc; + } + #endif + + // If the generator is started, allow sending a value. if (self->code_state.sp == self->code_state.state - 1) { if (send_value != mp_const_none) { mp_raise_TypeError("can't send non-None value to a just-started generator"); } } else { - #if MICROPY_PY_GENERATOR_PEND_THROW - // If exception is pending (set using .pend_throw()), process it now. - if (*self->code_state.sp != mp_const_none) { - throw_value = *self->code_state.sp; - *self->code_state.sp = MP_OBJ_NULL; - } else - #endif - { - *self->code_state.sp = send_value; - } + *self->code_state.sp = send_value; } - // We set self->globals=NULL while executing, for a sentinel to ensure the generator - // cannot be reentered during execution - if (self->globals == NULL) { - mp_raise_ValueError("generator already executing"); - } + // Mark as running + self->pend_exc = MP_OBJ_NULL; // Set up the correct globals context for the generator and execute it self->code_state.old_globals = mp_globals_get(); - mp_globals_set(self->globals); - self->globals = NULL; + mp_globals_set(self->code_state.fun_bc->globals); mp_vm_return_kind_t ret_kind; #if MICROPY_EMIT_NATIVE - if (self->code_state.exc_sp == NULL) { + if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) { // A native generator, with entry point 2 words into the "bytecode" pointer typedef uintptr_t (*mp_fun_native_gen_t)(void*, mp_obj_t); mp_fun_native_gen_t fun = MICROPY_MAKE_POINTER_CALLABLE((const void*)(self->code_state.fun_bc->bytecode + 2 * sizeof(uintptr_t))); @@ -181,9 +198,11 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ ret_kind = mp_execute_bytecode(&self->code_state, throw_value); } - self->globals = mp_globals_get(); mp_globals_set(self->code_state.old_globals); + // Mark as not running + self->pend_exc = mp_const_none; + switch (ret_kind) { case MP_VM_RETURN_NORMAL: default: @@ -297,16 +316,18 @@ STATIC mp_obj_t gen_instance_close(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(gen_instance_close_obj, gen_instance_close); +#if MICROPY_PY_GENERATOR_PEND_THROW STATIC mp_obj_t gen_instance_pend_throw(mp_obj_t self_in, mp_obj_t exc_in) { mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in); - if (self->code_state.sp == self->code_state.state - 1) { - mp_raise_TypeError("can't pend throw to just-started generator"); + if (self->pend_exc == MP_OBJ_NULL) { + mp_raise_ValueError("generator already executing"); } - mp_obj_t prev = *self->code_state.sp; - *self->code_state.sp = exc_in; + mp_obj_t prev = self->pend_exc; + self->pend_exc = exc_in; return prev; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_pend_throw_obj, gen_instance_pend_throw); +#endif STATIC const mp_rom_map_elem_t gen_instance_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&gen_instance_close_obj) }, diff --git a/python/src/py/objint.c b/python/src/py/objint.c index 6473767e4..2fdcf5864 100644 --- a/python/src/py/objint.c +++ b/python/src/py/objint.c @@ -137,7 +137,7 @@ STATIC mp_fp_as_int_class_t mp_classify_fp_as_int(mp_float_t val) { mp_obj_t mp_obj_new_int_from_float(mp_float_t val) { int cl = fpclassify(val); if (cl == FP_INFINITE) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OverflowError, "can't convert inf to int")); + mp_raise_msg(&mp_type_OverflowError, "can't convert inf to int"); } else if (cl == FP_NAN) { mp_raise_ValueError("can't convert NaN to int"); } else { diff --git a/python/src/py/objmodule.c b/python/src/py/objmodule.c index 4a07913c5..81558a02e 100644 --- a/python/src/py/objmodule.c +++ b/python/src/py/objmodule.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013-2019 Damien P. George * Copyright (c) 2014-2015 Paul Sokolovsky * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -26,6 +26,7 @@ */ #include +#include #include #include "py/objmodule.h" @@ -223,6 +224,9 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { #if MICROPY_PY_BTREE { MP_ROM_QSTR(MP_QSTR_btree), MP_ROM_PTR(&mp_module_btree) }, #endif +#if MICROPY_PY_BLUETOOTH + { MP_ROM_QSTR(MP_QSTR_ubluetooth), MP_ROM_PTR(&mp_module_ubluetooth) }, +#endif // extra builtin modules as defined by a port MICROPY_PORT_BUILTIN_MODULES @@ -235,14 +239,6 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { MP_DEFINE_CONST_MAP(mp_builtin_module_map, mp_builtin_module_table); -#if MICROPY_MODULE_WEAK_LINKS -STATIC const mp_rom_map_elem_t mp_builtin_module_weak_links_table[] = { - MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS -}; - -MP_DEFINE_CONST_MAP(mp_builtin_module_weak_links_map, mp_builtin_module_weak_links_table); -#endif - // returns MP_OBJ_NULL if not found mp_obj_t mp_module_get(qstr module_name) { mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map; @@ -267,6 +263,21 @@ void mp_module_register(qstr qst, mp_obj_t module) { mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = module; } +#if MICROPY_MODULE_WEAK_LINKS +// Search for u"foo" in built-in modules, return MP_OBJ_NULL if not found +mp_obj_t mp_module_search_umodule(const char *module_str) { + for (size_t i = 0; i < MP_ARRAY_SIZE(mp_builtin_module_table); ++i) { + const mp_map_elem_t *entry = (const mp_map_elem_t*)&mp_builtin_module_table[i]; + const char *key = qstr_str(MP_OBJ_QSTR_VALUE(entry->key)); + if (key[0] == 'u' && strcmp(&key[1], module_str) == 0) { + return (mp_obj_t)entry->value; + } + + } + return MP_OBJ_NULL; +} +#endif + #if MICROPY_MODULE_BUILTIN_INIT void mp_module_call_init(qstr module_name, mp_obj_t module_obj) { // Look for __init__ and call it if it exists diff --git a/python/src/py/objmodule.h b/python/src/py/objmodule.h index b7702ec50..33a0ff07e 100644 --- a/python/src/py/objmodule.h +++ b/python/src/py/objmodule.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013-2019 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,6 +34,8 @@ extern const mp_map_t mp_builtin_module_weak_links_map; mp_obj_t mp_module_get(qstr module_name); void mp_module_register(qstr qstr, mp_obj_t module); +mp_obj_t mp_module_search_umodule(const char *module_str); + #if MICROPY_MODULE_BUILTIN_INIT void mp_module_call_init(qstr module_name, mp_obj_t module_obj); #else diff --git a/python/src/py/objstr.c b/python/src/py/objstr.c index 1047ea94c..e221982c5 100644 --- a/python/src/py/objstr.c +++ b/python/src/py/objstr.c @@ -169,7 +169,7 @@ mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ // Check if a qstr with this data already exists qstr q = qstr_find_strn((const char*)str_data, str_len); - if (q != MP_QSTR_NULL) { + if (q != MP_QSTRnull) { return MP_OBJ_NEW_QSTR(q); } @@ -1913,9 +1913,6 @@ mp_int_t mp_obj_str_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_u return 0; } else { // can't write to a string - bufinfo->buf = NULL; - bufinfo->len = 0; - bufinfo->typecode = -1; return 1; } } @@ -2042,7 +2039,7 @@ mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, vstr_t *vstr) { // if not a bytes object, look if a qstr with this data already exists if (type == &mp_type_str) { qstr q = qstr_find_strn(vstr->buf, vstr->len); - if (q != MP_QSTR_NULL) { + if (q != MP_QSTRnull) { vstr_clear(vstr); vstr->alloc = 0; return MP_OBJ_NEW_QSTR(q); @@ -2067,7 +2064,7 @@ mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, vstr_t *vstr) { mp_obj_t mp_obj_new_str(const char* data, size_t len) { qstr q = qstr_find_strn(data, len); - if (q != MP_QSTR_NULL) { + if (q != MP_QSTRnull) { // qstr with this data already exists return MP_OBJ_NEW_QSTR(q); } else { diff --git a/python/src/py/objstringio.c b/python/src/py/objstringio.c index 89b679031..cca4a8129 100644 --- a/python/src/py/objstringio.c +++ b/python/src/py/objstringio.c @@ -70,9 +70,9 @@ STATIC mp_uint_t stringio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *er STATIC void stringio_copy_on_write(mp_obj_stringio_t *o) { const void *buf = o->vstr->buf; o->vstr->buf = m_new(char, o->vstr->len); - memcpy(o->vstr->buf, buf, o->vstr->len); o->vstr->fixed_buf = false; o->ref_obj = MP_OBJ_NULL; + memcpy(o->vstr->buf, buf, o->vstr->len); } STATIC mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { @@ -244,12 +244,6 @@ STATIC const mp_stream_p_t stringio_stream_p = { .is_text = true, }; -STATIC const mp_stream_p_t bytesio_stream_p = { - .read = stringio_read, - .write = stringio_write, - .ioctl = stringio_ioctl, -}; - const mp_obj_type_t mp_type_stringio = { { &mp_type_type }, .name = MP_QSTR_StringIO, @@ -262,6 +256,12 @@ const mp_obj_type_t mp_type_stringio = { }; #if MICROPY_PY_IO_BYTESIO +STATIC const mp_stream_p_t bytesio_stream_p = { + .read = stringio_read, + .write = stringio_write, + .ioctl = stringio_ioctl, +}; + const mp_obj_type_t mp_type_bytesio = { { &mp_type_type }, .name = MP_QSTR_BytesIO, diff --git a/python/src/py/objtuple.c b/python/src/py/objtuple.c index 39eb94023..740e0795b 100644 --- a/python/src/py/objtuple.c +++ b/python/src/py/objtuple.c @@ -31,6 +31,9 @@ #include "py/objtuple.h" #include "py/runtime.h" +// type check is done on getiter method to allow tuple, namedtuple, attrtuple +#define mp_obj_is_tuple_compatible(o) (mp_obj_get_type(o)->getiter == mp_obj_tuple_getiter) + /******************************************************************************/ /* tuple */ @@ -101,8 +104,7 @@ STATIC mp_obj_t mp_obj_tuple_make_new(const mp_obj_type_t *type_in, size_t n_arg // Don't pass MP_BINARY_OP_NOT_EQUAL here STATIC mp_obj_t tuple_cmp_helper(mp_uint_t op, mp_obj_t self_in, mp_obj_t another_in) { - // type check is done on getiter method to allow tuple, namedtuple, attrtuple - mp_check_self(mp_obj_get_type(self_in)->getiter == mp_obj_tuple_getiter); + mp_check_self(mp_obj_is_tuple_compatible(self_in)); mp_obj_type_t *another_type = mp_obj_get_type(another_in); mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); if (another_type->getiter != mp_obj_tuple_getiter) { @@ -249,7 +251,7 @@ mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items) { } void mp_obj_tuple_get(mp_obj_t self_in, size_t *len, mp_obj_t **items) { - assert(mp_obj_is_type(self_in, &mp_type_tuple)); + assert(mp_obj_is_tuple_compatible(self_in)); mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); *len = self->len; *items = &self->items[0]; diff --git a/python/src/py/objtype.c b/python/src/py/objtype.c index 3e65a32f0..bf089dc49 100644 --- a/python/src/py/objtype.c +++ b/python/src/py/objtype.c @@ -460,7 +460,7 @@ STATIC mp_obj_t instance_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } // Binary-op enum values not listed here will have the default value of 0 in the -// table, corresponding to MP_QSTR_NULL, and are therefore unsupported (a lookup will +// table, corresponding to MP_QSTRnull, and are therefore unsupported (a lookup will // fail). They can be added at the expense of code size for the qstr. // Qstrs for special methods are guaranteed to have a small value, so we use byte // type to represent them. @@ -478,6 +478,7 @@ const byte mp_binary_op_method_name[MP_BINARY_OP_NUM_RUNTIME] = { [MP_BINARY_OP_INPLACE_SUBTRACT] = MP_QSTR___isub__, #if MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS [MP_BINARY_OP_INPLACE_MULTIPLY] = MP_QSTR___imul__, + [MP_BINARY_OP_INPLACE_MAT_MULTIPLY] = MP_QSTR___imatmul__, [MP_BINARY_OP_INPLACE_FLOOR_DIVIDE] = MP_QSTR___ifloordiv__, [MP_BINARY_OP_INPLACE_TRUE_DIVIDE] = MP_QSTR___itruediv__, [MP_BINARY_OP_INPLACE_MODULO] = MP_QSTR___imod__, @@ -493,6 +494,7 @@ const byte mp_binary_op_method_name[MP_BINARY_OP_NUM_RUNTIME] = { [MP_BINARY_OP_SUBTRACT] = MP_QSTR___sub__, #if MICROPY_PY_ALL_SPECIAL_METHODS [MP_BINARY_OP_MULTIPLY] = MP_QSTR___mul__, + [MP_BINARY_OP_MAT_MULTIPLY] = MP_QSTR___matmul__, [MP_BINARY_OP_FLOOR_DIVIDE] = MP_QSTR___floordiv__, [MP_BINARY_OP_TRUE_DIVIDE] = MP_QSTR___truediv__, [MP_BINARY_OP_MODULO] = MP_QSTR___mod__, @@ -510,6 +512,7 @@ const byte mp_binary_op_method_name[MP_BINARY_OP_NUM_RUNTIME] = { [MP_BINARY_OP_REVERSE_SUBTRACT] = MP_QSTR___rsub__, #if MICROPY_PY_ALL_SPECIAL_METHODS [MP_BINARY_OP_REVERSE_MULTIPLY] = MP_QSTR___rmul__, + [MP_BINARY_OP_REVERSE_MAT_MULTIPLY] = MP_QSTR___rmatmul__, [MP_BINARY_OP_REVERSE_FLOOR_DIVIDE] = MP_QSTR___rfloordiv__, [MP_BINARY_OP_REVERSE_TRUE_DIVIDE] = MP_QSTR___rtruediv__, [MP_BINARY_OP_REVERSE_MODULO] = MP_QSTR___rmod__, @@ -1011,6 +1014,21 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { dest[0] = MP_OBJ_NEW_QSTR(self->name); return; } + if (attr == MP_QSTR___bases__) { + if (self == &mp_type_object) { + dest[0] = mp_const_empty_tuple; + return; + } + mp_obj_t parent_obj = self->parent ? MP_OBJ_FROM_PTR(self->parent) : MP_OBJ_FROM_PTR(&mp_type_object); + #if MICROPY_MULTIPLE_INHERITANCE + if (mp_obj_is_type(parent_obj, &mp_type_tuple)) { + dest[0] = parent_obj; + return; + } + #endif + dest[0] = mp_obj_new_tuple(1, &parent_obj); + return; + } #endif struct class_lookup_data lookup = { .obj = (mp_obj_instance_t*)self, diff --git a/python/src/py/parse.c b/python/src/py/parse.c index 66110e7c3..82b5413e5 100644 --- a/python/src/py/parse.c +++ b/python/src/py/parse.c @@ -135,8 +135,8 @@ STATIC const uint16_t rule_arg_combined_table[] = { #define RULE_EXPAND(x) x #define RULE_PADDING(rule, ...) RULE_PADDING2(rule, __VA_ARGS__, RULE_PADDING_IDS(rule)) #define RULE_PADDING2(rule, ...) RULE_EXPAND(RULE_PADDING3(rule, __VA_ARGS__)) -#define RULE_PADDING3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) __VA_ARGS__ -#define RULE_PADDING_IDS(r) PAD12_##r, PAD11_##r, PAD10_##r, PAD9_##r, PAD8_##r, PAD7_##r, PAD6_##r, PAD5_##r, PAD4_##r, PAD3_##r, PAD2_##r, PAD1_##r, +#define RULE_PADDING3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) __VA_ARGS__ +#define RULE_PADDING_IDS(r) PAD13_##r, PAD12_##r, PAD11_##r, PAD10_##r, PAD9_##r, PAD8_##r, PAD7_##r, PAD6_##r, PAD5_##r, PAD4_##r, PAD3_##r, PAD2_##r, PAD1_##r, // Use an enum to create constants specifying how much room a rule takes in rule_arg_combined_table enum { @@ -155,8 +155,8 @@ enum { // Macro to compute the start of a rule in rule_arg_combined_table #define RULE_ARG_OFFSET(rule, ...) RULE_ARG_OFFSET2(rule, __VA_ARGS__, RULE_ARG_OFFSET_IDS(rule)) #define RULE_ARG_OFFSET2(rule, ...) RULE_EXPAND(RULE_ARG_OFFSET3(rule, __VA_ARGS__)) -#define RULE_ARG_OFFSET3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) _13 -#define RULE_ARG_OFFSET_IDS(r) PAD12_##r, PAD11_##r, PAD10_##r, PAD9_##r, PAD8_##r, PAD7_##r, PAD6_##r, PAD5_##r, PAD4_##r, PAD3_##r, PAD2_##r, PAD1_##r, PAD0_##r, +#define RULE_ARG_OFFSET3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) _14 +#define RULE_ARG_OFFSET_IDS(r) PAD13_##r, PAD12_##r, PAD11_##r, PAD10_##r, PAD9_##r, PAD8_##r, PAD7_##r, PAD6_##r, PAD5_##r, PAD4_##r, PAD3_##r, PAD2_##r, PAD1_##r, PAD0_##r, // Use the above enum values to create a table of offsets for each rule's arg // data, which indexes rule_arg_combined_table. The offsets require 9 bits of @@ -502,7 +502,7 @@ STATIC void push_result_token(parser_t *parser, uint8_t rule_id) { } else if (lex->tok_kind == MP_TOKEN_STRING || lex->tok_kind == MP_TOKEN_BYTES) { // Don't automatically intern all strings/bytes. doc strings (which are usually large) // will be discarded by the compiler, and so we shouldn't intern them. - qstr qst = MP_QSTR_NULL; + qstr qst = MP_QSTRnull; if (lex->vstr.len <= MICROPY_ALLOC_PARSE_INTERN_STRING_LEN) { // intern short strings qst = qstr_from_strn(lex->vstr.buf, lex->vstr.len); @@ -510,7 +510,7 @@ STATIC void push_result_token(parser_t *parser, uint8_t rule_id) { // check if this string is already interned qst = qstr_find_strn(lex->vstr.buf, lex->vstr.len); } - if (qst != MP_QSTR_NULL) { + if (qst != MP_QSTRnull) { // qstr exists, make a leaf node pn = mp_parse_node_new_leaf(lex->tok_kind == MP_TOKEN_STRING ? MP_PARSE_NODE_STRING : MP_PARSE_NODE_BYTES, qst); } else { @@ -632,7 +632,7 @@ STATIC bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { } else if (rule_id == RULE_shift_expr || rule_id == RULE_arith_expr || rule_id == RULE_term) { - // folding for binary ops: << >> + - * / % // + // folding for binary ops: << >> + - * @ / % // mp_parse_node_t pn = peek_result(parser, num_args - 1); if (!mp_parse_node_get_int_maybe(pn, &arg0)) { return false; @@ -644,23 +644,11 @@ STATIC bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { return false; } mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, i)); - static const uint8_t token_to_op[] = { - MP_BINARY_OP_ADD, - MP_BINARY_OP_SUBTRACT, - MP_BINARY_OP_MULTIPLY, - 255,//MP_BINARY_OP_POWER, - 255,//MP_BINARY_OP_TRUE_DIVIDE, - MP_BINARY_OP_FLOOR_DIVIDE, - MP_BINARY_OP_MODULO, - 255,//MP_BINARY_OP_LESS - MP_BINARY_OP_LSHIFT, - 255,//MP_BINARY_OP_MORE - MP_BINARY_OP_RSHIFT, - }; - mp_binary_op_t op = token_to_op[tok - MP_TOKEN_OP_PLUS]; - if (op == (mp_binary_op_t)255) { + if (tok == MP_TOKEN_OP_AT || tok == MP_TOKEN_OP_SLASH || tok == MP_TOKEN_OP_DBL_STAR) { + // Can't fold @ or / or ** return false; } + mp_binary_op_t op = MP_BINARY_OP_LSHIFT + (tok - MP_TOKEN_OP_DBL_LESS); int rhs_sign = mp_obj_int_sign(arg1); if (op <= MP_BINARY_OP_RSHIFT) { // << and >> can't have negative rhs @@ -683,13 +671,11 @@ STATIC bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { } mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, 1)); mp_unary_op_t op; - if (tok == MP_TOKEN_OP_PLUS) { - op = MP_UNARY_OP_POSITIVE; - } else if (tok == MP_TOKEN_OP_MINUS) { - op = MP_UNARY_OP_NEGATIVE; - } else { - assert(tok == MP_TOKEN_OP_TILDE); // should be + if (tok == MP_TOKEN_OP_TILDE) { op = MP_UNARY_OP_INVERT; + } else { + assert(tok == MP_TOKEN_OP_PLUS || tok == MP_TOKEN_OP_MINUS); // should be + op = MP_UNARY_OP_POSITIVE + (tok - MP_TOKEN_OP_PLUS); } arg0 = mp_unary_op(op, arg0); @@ -719,7 +705,7 @@ STATIC bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { mp_obj_t exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, "constant must be an integer"); mp_obj_exception_add_traceback(exc, parser->lexer->source_name, - ((mp_parse_node_struct_t*)pn1)->source_line, MP_QSTR_NULL); + ((mp_parse_node_struct_t*)pn1)->source_line, MP_QSTRnull); nlr_raise(exc); } @@ -1152,7 +1138,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { } // add traceback to give info about file name and location // we don't have a 'block' name, so just pass the NULL qstr to indicate this - mp_obj_exception_add_traceback(exc, lex->source_name, lex->tok_line, MP_QSTR_NULL); + mp_obj_exception_add_traceback(exc, lex->source_name, lex->tok_line, MP_QSTRnull); nlr_raise(exc); } diff --git a/python/src/py/parsenum.c b/python/src/py/parsenum.c index ae9b83419..2b01b7259 100644 --- a/python/src/py/parsenum.c +++ b/python/src/py/parsenum.c @@ -41,7 +41,7 @@ STATIC NORETURN void raise_exc(mp_obj_t exc, mp_lexer_t *lex) { // exception's type from ValueError to SyntaxError and add traceback info if (lex != NULL) { ((mp_obj_base_t*)MP_OBJ_TO_PTR(exc))->type = &mp_type_SyntaxError; - mp_obj_exception_add_traceback(exc, lex->source_name, lex->tok_line, MP_QSTR_NULL); + mp_obj_exception_add_traceback(exc, lex->source_name, lex->tok_line, MP_QSTRnull); } nlr_raise(exc); } diff --git a/python/src/py/persistentcode.c b/python/src/py/persistentcode.c index cc6e161f4..7a8a94b5a 100644 --- a/python/src/py/persistentcode.c +++ b/python/src/py/persistentcode.c @@ -30,9 +30,10 @@ #include #include "py/reader.h" -#include "py/emitglue.h" +#include "py/nativeglue.h" #include "py/persistentcode.h" -#include "py/bc.h" +#include "py/bc0.h" +#include "py/objstr.h" #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE @@ -40,41 +41,6 @@ #define QSTR_LAST_STATIC MP_QSTR_zip -// Macros to encode/decode flags to/from the feature byte -#define MPY_FEATURE_ENCODE_FLAGS(flags) (flags) -#define MPY_FEATURE_DECODE_FLAGS(feat) ((feat) & 3) - -// Macros to encode/decode native architecture to/from the feature byte -#define MPY_FEATURE_ENCODE_ARCH(arch) ((arch) << 2) -#define MPY_FEATURE_DECODE_ARCH(feat) ((feat) >> 2) - -// The feature flag bits encode the compile-time config options that -// affect the generate bytecode. -#define MPY_FEATURE_FLAGS ( \ - ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) << 0) \ - | ((MICROPY_PY_BUILTINS_STR_UNICODE) << 1) \ - ) -// This is a version of the flags that can be configured at runtime. -#define MPY_FEATURE_FLAGS_DYNAMIC ( \ - ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) << 0) \ - | ((MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC) << 1) \ - ) - -// Define the host architecture -#if MICROPY_EMIT_X86 -#define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X86) -#elif MICROPY_EMIT_X64 -#define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X64) -#elif MICROPY_EMIT_THUMB -#define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7M) -#elif MICROPY_EMIT_ARM -#define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV6) -#elif MICROPY_EMIT_XTENSA -#define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSA) -#else -#define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_NONE) -#endif - #if MICROPY_DYNAMIC_COMPILER #define MPY_FEATURE_ARCH_DYNAMIC mp_dynamic_compiler.native_arch #else @@ -157,33 +123,38 @@ typedef struct _bytecode_prelude_t { uint code_info_size; } bytecode_prelude_t; -#if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_EMIT_NATIVE - // ip will point to start of opcodes -// ip2 will point to simple_name, source_file qstrs -STATIC void extract_prelude(const byte **ip, const byte **ip2, bytecode_prelude_t *prelude) { - prelude->n_state = mp_decode_uint(ip); - prelude->n_exc_stack = mp_decode_uint(ip); - prelude->scope_flags = *(*ip)++; - prelude->n_pos_args = *(*ip)++; - prelude->n_kwonly_args = *(*ip)++; - prelude->n_def_pos_args = *(*ip)++; - *ip2 = *ip; - prelude->code_info_size = mp_decode_uint(ip2); - *ip += prelude->code_info_size; - while (*(*ip)++ != 255) { - } +// return value will point to simple_name, source_file qstrs +STATIC byte *extract_prelude(const byte **ip, bytecode_prelude_t *prelude) { + MP_BC_PRELUDE_SIG_DECODE(*ip); + prelude->n_state = n_state; + prelude->n_exc_stack = n_exc_stack; + prelude->scope_flags = scope_flags; + prelude->n_pos_args = n_pos_args; + prelude->n_kwonly_args = n_kwonly_args; + prelude->n_def_pos_args = n_def_pos_args; + MP_BC_PRELUDE_SIZE_DECODE(*ip); + byte *ip_info = (byte*)*ip; + *ip += n_info; + *ip += n_cell; + return ip_info; } -#endif - #endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE #if MICROPY_PERSISTENT_CODE_LOAD #include "py/parsenum.h" -#if MICROPY_EMIT_NATIVE +STATIC int read_byte(mp_reader_t *reader); +STATIC size_t read_uint(mp_reader_t *reader, byte **out); + +#if MICROPY_EMIT_MACHINE_CODE + +typedef struct _reloc_info_t { + mp_reader_t *reader; + mp_uint_t *const_table; +} reloc_info_t; #if MICROPY_EMIT_THUMB STATIC void asm_thumb_rewrite_mov(uint8_t *pc, uint16_t val) { @@ -200,7 +171,7 @@ STATIC void arch_link_qstr(uint8_t *pc, bool is_obj, qstr qst) { if (is_obj) { val = (mp_uint_t)MP_OBJ_NEW_QSTR(qst); } - #if MICROPY_EMIT_X86 || MICROPY_EMIT_X64 || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA + #if MICROPY_EMIT_X86 || MICROPY_EMIT_X64 || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA || MICROPY_EMIT_XTENSAWIN pc[0] = val & 0xff; pc[1] = (val >> 8) & 0xff; pc[2] = (val >> 16) & 0xff; @@ -217,6 +188,52 @@ STATIC void arch_link_qstr(uint8_t *pc, bool is_obj, qstr qst) { #endif } +void mp_native_relocate(void *ri_in, uint8_t *text, uintptr_t reloc_text) { + // Relocate native code + reloc_info_t *ri = ri_in; + uint8_t op; + uintptr_t *addr_to_adjust = NULL; + while ((op = read_byte(ri->reader)) != 0xff) { + if (op & 1) { + // Point to new location to make adjustments + size_t addr = read_uint(ri->reader, NULL); + if ((addr & 1) == 0) { + // Point to somewhere in text + addr_to_adjust = &((uintptr_t*)text)[addr >> 1]; + } else { + // Point to somewhere in rodata + addr_to_adjust = &((uintptr_t*)ri->const_table[1])[addr >> 1]; + } + } + op >>= 1; + uintptr_t dest; + size_t n = 1; + if (op <= 5) { + if (op & 1) { + // Read in number of adjustments to make + n = read_uint(ri->reader, NULL); + } + op >>= 1; + if (op == 0) { + // Destination is text + dest = reloc_text; + } else { + // Destination is rodata (op=1) or bss (op=1 if no rodata, else op=2) + dest = ri->const_table[op]; + } + } else if (op == 6) { + // Destination is mp_fun_table itself + dest = (uintptr_t)&mp_fun_table; + } else { + // Destination is an entry in mp_fun_table + dest = ((uintptr_t*)&mp_fun_table)[op - 7]; + } + while (n--) { + *addr_to_adjust++ += dest; + } + } +} + #endif STATIC int read_byte(mp_reader_t *reader) { @@ -284,20 +301,28 @@ STATIC mp_obj_t load_obj(mp_reader_t *reader) { } } -STATIC void load_prelude(mp_reader_t *reader, byte **ip, byte **ip2, bytecode_prelude_t *prelude) { - prelude->n_state = read_uint(reader, ip); - prelude->n_exc_stack = read_uint(reader, ip); - read_bytes(reader, *ip, 4); - prelude->scope_flags = *(*ip)++; - prelude->n_pos_args = *(*ip)++; - prelude->n_kwonly_args = *(*ip)++; - prelude->n_def_pos_args = *(*ip)++; - *ip2 = *ip; - prelude->code_info_size = read_uint(reader, ip2); - read_bytes(reader, *ip2, prelude->code_info_size - (*ip2 - *ip)); - *ip += prelude->code_info_size; - while ((*(*ip)++ = read_byte(reader)) != 255) { - } +STATIC void load_prelude_qstrs(mp_reader_t *reader, qstr_window_t *qw, byte *ip) { + qstr simple_name = load_qstr(reader, qw); + ip[0] = simple_name; ip[1] = simple_name >> 8; + qstr source_file = load_qstr(reader, qw); + ip[2] = source_file; ip[3] = source_file >> 8; +} + +STATIC void load_prelude(mp_reader_t *reader, qstr_window_t *qw, byte **ip, bytecode_prelude_t *prelude) { + // Read in the prelude header + byte *ip_read = *ip; + read_uint(reader, &ip_read); // read in n_state/etc (is effectively a var-uint) + read_uint(reader, &ip_read); // read in n_info/n_cell (is effectively a var-uint) + + // Prelude header has been read into *ip, now decode and extract values from it + extract_prelude((const byte**)ip, prelude); + + // Load qstrs in prelude + load_prelude_qstrs(reader, qw, ip_read); + ip_read += 4; + + // Read remaining code info + read_bytes(reader, ip_read, *ip - ip_read); } STATIC void load_bytecode(mp_reader_t *reader, qstr_window_t *qw, byte *ip, byte *ip_top) { @@ -307,12 +332,12 @@ STATIC void load_bytecode(mp_reader_t *reader, qstr_window_t *qw, byte *ip, byte uint f = mp_opcode_format(ip, &sz, false); ++ip; --sz; - if (f == MP_OPCODE_QSTR) { + if (f == MP_BC_FORMAT_QSTR) { qstr qst = load_qstr(reader, qw); *ip++ = qst; *ip++ = qst >> 8; sz -= 2; - } else if (f == MP_OPCODE_VAR_UINT) { + } else if (f == MP_BC_FORMAT_VAR_UINT) { while ((*ip++ = read_byte(reader)) & 0x80) { } } @@ -327,17 +352,16 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { int kind = (kind_len & 3) + MP_CODE_BYTECODE; size_t fun_data_len = kind_len >> 2; - #if !MICROPY_EMIT_NATIVE + #if !MICROPY_EMIT_MACHINE_CODE if (kind != MP_CODE_BYTECODE) { mp_raise_ValueError("incompatible .mpy file"); } #endif uint8_t *fun_data = NULL; - byte *ip2; bytecode_prelude_t prelude = {0}; - #if MICROPY_EMIT_NATIVE - size_t prelude_offset; + #if MICROPY_EMIT_MACHINE_CODE + size_t prelude_offset = 0; mp_uint_t type_sig = 0; size_t n_qstr_link = 0; #endif @@ -348,12 +372,12 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { // Load prelude byte *ip = fun_data; - load_prelude(reader, &ip, &ip2, &prelude); + load_prelude(reader, qw, &ip, &prelude); // Load bytecode load_bytecode(reader, qw, ip, fun_data + fun_data_len); - #if MICROPY_EMIT_NATIVE + #if MICROPY_EMIT_MACHINE_CODE } else { // Allocate memory for native data and load it size_t fun_alloc; @@ -371,6 +395,9 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { // Generic 16-bit link dest[0] = qst & 0xff; dest[1] = (qst >> 8) & 0xff; + } else if ((off & 3) == 3) { + // Generic, aligned qstr-object link + *(mp_obj_t*)dest = MP_OBJ_NEW_QSTR(qst); } else { // Architecture-specific link arch_link_qstr(dest, (off & 3) == 2, qst); @@ -382,7 +409,9 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { // Extract prelude for later use prelude_offset = read_uint(reader, NULL); const byte *ip = fun_data + prelude_offset; - extract_prelude(&ip, (const byte**)&ip2, &prelude); + byte *ip_info = extract_prelude(&ip, &prelude); + // Load qstrs in prelude + load_prelude_qstrs(reader, qw, ip_info); } else { // Load basic scope info for viper and asm prelude.scope_flags = read_uint(reader, NULL); @@ -396,27 +425,31 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { #endif } - if (kind == MP_CODE_BYTECODE || kind == MP_CODE_NATIVE_PY) { - // Load qstrs in prelude - qstr simple_name = load_qstr(reader, qw); - qstr source_file = load_qstr(reader, qw); - ip2[0] = simple_name; ip2[1] = simple_name >> 8; - ip2[2] = source_file; ip2[3] = source_file >> 8; - } - + size_t n_obj = 0; + size_t n_raw_code = 0; mp_uint_t *const_table = NULL; + if (kind != MP_CODE_NATIVE_ASM) { // Load constant table for bytecode, native and viper // Number of entries in constant table - size_t n_obj = read_uint(reader, NULL); - size_t n_raw_code = read_uint(reader, NULL); + n_obj = read_uint(reader, NULL); + n_raw_code = read_uint(reader, NULL); // Allocate constant table size_t n_alloc = prelude.n_pos_args + prelude.n_kwonly_args + n_obj + n_raw_code; + #if MICROPY_EMIT_MACHINE_CODE if (kind != MP_CODE_BYTECODE) { ++n_alloc; // additional entry for mp_fun_table + if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRODATA) { + ++n_alloc; // additional entry for rodata + } + if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERBSS) { + ++n_alloc; // additional entry for BSS + } } + #endif + const_table = m_new(mp_uint_t, n_alloc); mp_uint_t *ct = const_table; @@ -426,10 +459,25 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { *ct++ = (mp_uint_t)MP_OBJ_NEW_QSTR(load_qstr(reader, qw)); } - #if MICROPY_EMIT_NATIVE + #if MICROPY_EMIT_MACHINE_CODE if (kind != MP_CODE_BYTECODE) { // Populate mp_fun_table entry - *ct++ = (mp_uint_t)(uintptr_t)mp_fun_table; + *ct++ = (mp_uint_t)(uintptr_t)&mp_fun_table; + + // Allocate and load rodata if needed + if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRODATA) { + size_t size = read_uint(reader, NULL); + uint8_t *rodata = m_new(uint8_t, size); + read_bytes(reader, rodata, size); + *ct++ = (uintptr_t)rodata; + } + + // Allocate BSS if needed + if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERBSS) { + size_t size = read_uint(reader, NULL); + uint8_t *bss = m_new0(uint8_t, size); + *ct++ = (uintptr_t)bss; + } } #endif @@ -445,6 +493,7 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { // Create raw_code and return it mp_raw_code_t *rc = mp_emit_glue_new_raw_code(); if (kind == MP_CODE_BYTECODE) { + // Assign bytecode to raw code object mp_emit_glue_assign_bytecode(rc, fun_data, #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS fun_data_len, @@ -455,12 +504,20 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { #endif prelude.scope_flags); - #if MICROPY_EMIT_NATIVE + #if MICROPY_EMIT_MACHINE_CODE } else { + // Relocate and commit code to executable address space + reloc_info_t ri = {reader, const_table}; #if defined(MP_PLAT_COMMIT_EXEC) - fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len); + void *opt_ri = (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL; + fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri); + #else + if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRELOC) { + mp_native_relocate(&ri, fun_data, (uintptr_t)fun_data); + } #endif + // Assign native code to raw code object mp_emit_glue_assign_native(rc, kind, fun_data, fun_data_len, const_table, #if MICROPY_PERSISTENT_CODE_SAVE @@ -484,9 +541,11 @@ mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) { || read_uint(reader, NULL) > QSTR_WINDOW_SIZE) { mp_raise_ValueError("incompatible .mpy file"); } - if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE - && MPY_FEATURE_DECODE_ARCH(header[2]) != MPY_FEATURE_ARCH) { - mp_raise_ValueError("incompatible .mpy arch"); + if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) { + byte arch = MPY_FEATURE_DECODE_ARCH(header[2]); + if (!MPY_FEATURE_ARCH_TEST(arch)) { + mp_raise_ValueError("incompatible .mpy arch"); + } } qstr_window_t qw; qw.idx = 0; @@ -593,11 +652,16 @@ STATIC void save_obj(mp_print_t *print, mp_obj_t o) { } } +STATIC void save_prelude_qstrs(mp_print_t *print, qstr_window_t *qw, const byte *ip) { + save_qstr(print, qw, ip[0] | (ip[1] << 8)); // simple_name + save_qstr(print, qw, ip[2] | (ip[3] << 8)); // source_file +} + STATIC void save_bytecode(mp_print_t *print, qstr_window_t *qw, const byte *ip, const byte *ip_top) { while (ip < ip_top) { size_t sz; uint f = mp_opcode_format(ip, &sz, true); - if (f == MP_OPCODE_QSTR) { + if (f == MP_BC_FORMAT_QSTR) { mp_print_bytes(print, ip, 1); qstr qst = ip[1] | (ip[2] << 8); save_qstr(print, qw, qst); @@ -613,19 +677,23 @@ STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc, qstr_window_t *q // Save function kind and data length mp_print_uint(print, (rc->fun_data_len << 2) | (rc->kind - MP_CODE_BYTECODE)); - const byte *ip2; bytecode_prelude_t prelude; if (rc->kind == MP_CODE_BYTECODE) { - // Save prelude + // Extract prelude const byte *ip = rc->fun_data; - extract_prelude(&ip, &ip2, &prelude); - size_t prelude_len = ip - (const byte*)rc->fun_data; - const byte *ip_top = (const byte*)rc->fun_data + rc->fun_data_len; - mp_print_bytes(print, rc->fun_data, prelude_len); + const byte *ip_info = extract_prelude(&ip, &prelude); + + // Save prelude + mp_print_bytes(print, rc->fun_data, ip_info - (const byte*)rc->fun_data); + save_prelude_qstrs(print, qstr_window, ip_info); + ip_info += 4; + mp_print_bytes(print, ip_info, ip - ip_info); // Save bytecode + const byte *ip_top = (const byte*)rc->fun_data + rc->fun_data_len; save_bytecode(print, qstr_window, ip, ip_top); + #if MICROPY_EMIT_MACHINE_CODE } else { // Save native code mp_print_bytes(print, rc->fun_data, rc->fun_data_len); @@ -640,13 +708,16 @@ STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc, qstr_window_t *q } if (rc->kind == MP_CODE_NATIVE_PY) { - // Save prelude size, and extract prelude for later use + // Save prelude size mp_print_uint(print, rc->prelude_offset); + + // Extract prelude and save qstrs in prelude const byte *ip = (const byte*)rc->fun_data + rc->prelude_offset; - extract_prelude(&ip, &ip2, &prelude); + const byte *ip_info = extract_prelude(&ip, &prelude); + save_prelude_qstrs(print, qstr_window, ip_info); } else { // Save basic scope info for viper and asm - mp_print_uint(print, rc->scope_flags); + mp_print_uint(print, rc->scope_flags & MP_SCOPE_FLAG_ALL_SIG); prelude.n_pos_args = 0; prelude.n_kwonly_args = 0; if (rc->kind == MP_CODE_NATIVE_ASM) { @@ -654,12 +725,7 @@ STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc, qstr_window_t *q mp_print_uint(print, rc->type_sig); } } - } - - if (rc->kind == MP_CODE_BYTECODE || rc->kind == MP_CODE_NATIVE_PY) { - // Save qstrs in prelude - save_qstr(print, qstr_window, ip2[0] | (ip2[1] << 8)); // simple_name - save_qstr(print, qstr_window, ip2[2] | (ip2[3] << 8)); // source_file + #endif } if (rc->kind != MP_CODE_NATIVE_ASM) { @@ -699,9 +765,8 @@ STATIC bool mp_raw_code_has_native(mp_raw_code_t *rc) { } const byte *ip = rc->fun_data; - const byte *ip2; bytecode_prelude_t prelude; - extract_prelude(&ip, &ip2, &prelude); + extract_prelude(&ip, &prelude); const mp_uint_t *const_table = rc->const_table + prelude.n_pos_args + prelude.n_kwonly_args @@ -748,7 +813,7 @@ void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) { // here we define mp_raw_code_save_file depending on the port // TODO abstract this away properly -#if defined(__i386__) || defined(__x86_64__) || defined(__unix__) +#if defined(__i386__) || defined(__x86_64__) || defined(_WIN32) || defined(__unix__) #include #include diff --git a/python/src/py/persistentcode.h b/python/src/py/persistentcode.h index 2074c64fe..8769ef584 100644 --- a/python/src/py/persistentcode.h +++ b/python/src/py/persistentcode.h @@ -31,7 +31,63 @@ #include "py/emitglue.h" // The current version of .mpy files -#define MPY_VERSION 4 +#define MPY_VERSION 5 + +// Macros to encode/decode flags to/from the feature byte +#define MPY_FEATURE_ENCODE_FLAGS(flags) (flags) +#define MPY_FEATURE_DECODE_FLAGS(feat) ((feat) & 3) + +// Macros to encode/decode native architecture to/from the feature byte +#define MPY_FEATURE_ENCODE_ARCH(arch) ((arch) << 2) +#define MPY_FEATURE_DECODE_ARCH(feat) ((feat) >> 2) + +// The feature flag bits encode the compile-time config options that +// affect the generate bytecode. +#define MPY_FEATURE_FLAGS ( \ + ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) << 0) \ + | ((MICROPY_PY_BUILTINS_STR_UNICODE) << 1) \ + ) +// This is a version of the flags that can be configured at runtime. +#define MPY_FEATURE_FLAGS_DYNAMIC ( \ + ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) << 0) \ + | ((MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC) << 1) \ + ) + +// Define the host architecture +#if MICROPY_EMIT_X86 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X86) +#elif MICROPY_EMIT_X64 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X64) +#elif MICROPY_EMIT_THUMB + #if defined(__thumb2__) + #if defined(__ARM_FP) && (__ARM_FP & 8) == 8 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMDP) + #elif defined(__ARM_FP) && (__ARM_FP & 4) == 4 + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EMSP) + #else + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7EM) + #endif + #else + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV7M) + #endif + #define MPY_FEATURE_ARCH_TEST(x) (MP_NATIVE_ARCH_ARMV6M <= (x) && (x) <= MPY_FEATURE_ARCH) +#elif MICROPY_EMIT_ARM + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_ARMV6) +#elif MICROPY_EMIT_XTENSA + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSA) +#elif MICROPY_EMIT_XTENSAWIN + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_XTENSAWIN) +#else + #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_NONE) +#endif + +#ifndef MPY_FEATURE_ARCH_TEST +#define MPY_FEATURE_ARCH_TEST(x) ((x) == MPY_FEATURE_ARCH) +#endif + +// 16-bit little-endian integer with the second and third bytes of supported .mpy files +#define MPY_FILE_HEADER_INT (MPY_VERSION \ + | (MPY_FEATURE_ENCODE_FLAGS(MPY_FEATURE_FLAGS) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH)) << 8) enum { MP_NATIVE_ARCH_NONE = 0, @@ -44,6 +100,7 @@ enum { MP_NATIVE_ARCH_ARMV7EMSP, MP_NATIVE_ARCH_ARMV7EMDP, MP_NATIVE_ARCH_XTENSA, + MP_NATIVE_ARCH_XTENSAWIN, }; mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader); @@ -53,4 +110,6 @@ mp_raw_code_t *mp_raw_code_load_file(const char *filename); void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print); void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename); +void mp_native_relocate(void *reloc, uint8_t *text, uintptr_t reloc_text); + #endif // MICROPY_INCLUDED_PY_PERSISTENTCODE_H diff --git a/python/src/py/profile.c b/python/src/py/profile.c new file mode 100644 index 000000000..72726cdf5 --- /dev/null +++ b/python/src/py/profile.c @@ -0,0 +1,984 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT HOLDERS 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 "py/profile.h" +#include "py/bc0.h" +#include "py/gc.h" + +#if MICROPY_PY_SYS_SETTRACE + +#define prof_trace_cb MP_STATE_THREAD(prof_trace_callback) + +STATIC uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc) { + const mp_bytecode_prelude_t *prelude = &rc->prelude; + return mp_bytecode_get_source_line(prelude->line_info, bc); +} + +void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelude) { + const byte *ip = bytecode; + + MP_BC_PRELUDE_SIG_DECODE(ip); + prelude->n_state = n_state; + prelude->n_exc_stack = n_exc_stack; + prelude->scope_flags = scope_flags; + prelude->n_pos_args = n_pos_args; + prelude->n_kwonly_args = n_kwonly_args; + prelude->n_def_pos_args = n_def_pos_args; + + MP_BC_PRELUDE_SIZE_DECODE(ip); + + prelude->line_info = ip + 4; + prelude->opcodes = ip + n_info + n_cell; + + qstr block_name = ip[0] | (ip[1] << 8); + qstr source_file = ip[2] | (ip[3] << 8); + prelude->qstr_block_name = block_name; + prelude->qstr_source_file = source_file; +} + +/******************************************************************************/ +// code object + +STATIC void code_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_code_t *o = MP_OBJ_TO_PTR(o_in); + const mp_raw_code_t *rc = o->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + mp_printf(print, + "", + prelude->qstr_block_name, + o, + prelude->qstr_source_file, + rc->line_of_definition + ); +} + +STATIC mp_obj_tuple_t* code_consts(const mp_raw_code_t *rc) { + const mp_bytecode_prelude_t *prelude = &rc->prelude; + int start = prelude->n_pos_args + prelude->n_kwonly_args + rc->n_obj; + int stop = prelude->n_pos_args + prelude->n_kwonly_args + rc->n_obj + rc->n_raw_code; + mp_obj_tuple_t *consts = MP_OBJ_TO_PTR(mp_obj_new_tuple(stop - start + 1, NULL)); + + size_t const_no = 0; + for (int i = start; i < stop; ++i) { + mp_obj_t code = mp_obj_new_code((const mp_raw_code_t*)MP_OBJ_TO_PTR(rc->const_table[i])); + if (code == MP_OBJ_NULL) { + m_malloc_fail(sizeof(mp_obj_code_t)); + } + consts->items[const_no++] = code; + } + consts->items[const_no++] = mp_const_none; + + return consts; +} + +STATIC mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) { + // const mp_bytecode_prelude_t *prelude = &rc->prelude; + uint start = 0; + uint stop = rc->fun_data_len - start; + + uint last_lineno = mp_prof_bytecode_lineno(rc, start); + uint lasti = 0; + + const uint buffer_chunk_size = (stop-start) >> 2; // heuristic magic + uint buffer_size = buffer_chunk_size; + byte *buffer = m_new(byte, buffer_size); + uint buffer_index = 0; + + for (uint i = start; i < stop; ++i) { + uint lineno = mp_prof_bytecode_lineno(rc, i); + size_t line_diff = lineno - last_lineno; + if (line_diff > 0) { + uint instr_diff = (i - start) - lasti; + + assert(instr_diff < 256); + assert(line_diff < 256); + + if (buffer_index + 2 > buffer_size) { + buffer = m_renew(byte, buffer, buffer_size, buffer_size + buffer_chunk_size); + buffer_size = buffer_size + buffer_chunk_size; + } + last_lineno = lineno; + lasti = i - start; + buffer[buffer_index++] = instr_diff; + buffer[buffer_index++] = line_diff; + } + } + + mp_obj_t o = mp_obj_new_bytes(buffer, buffer_index); + m_del(byte, buffer, buffer_size); + return o; +} + +STATIC void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + mp_obj_code_t *o = MP_OBJ_TO_PTR(self_in); + const mp_raw_code_t *rc = o->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + switch(attr) { + case MP_QSTR_co_code: + dest[0] = mp_obj_new_bytes( + (void*)prelude->opcodes, + rc->fun_data_len - (prelude->opcodes - (const byte*)rc->fun_data) + ); + break; + case MP_QSTR_co_consts: + dest[0] = MP_OBJ_FROM_PTR(code_consts(rc)); + break; + case MP_QSTR_co_filename: + dest[0] = MP_OBJ_NEW_QSTR(prelude->qstr_source_file); + break; + case MP_QSTR_co_firstlineno: + dest[0] = MP_OBJ_NEW_SMALL_INT(mp_prof_bytecode_lineno(rc, 0)); + break; + case MP_QSTR_co_name: + dest[0] = MP_OBJ_NEW_QSTR(prelude->qstr_block_name); + break; + case MP_QSTR_co_names: + dest[0] = MP_OBJ_FROM_PTR(o->dict_locals); + break; + case MP_QSTR_co_lnotab: + if (!o->lnotab) { + o->lnotab = raw_code_lnotab(rc); + } + dest[0] = o->lnotab; + break; + } +} + +const mp_obj_type_t mp_type_code = { + { &mp_type_type }, + .name = MP_QSTR_code, + .print = code_print, + .unary_op = mp_generic_unary_op, + .attr = code_attr, +}; + +mp_obj_t mp_obj_new_code(const mp_raw_code_t *rc) { + mp_obj_code_t *o = m_new_obj_maybe(mp_obj_code_t); + if (o == NULL) { + return MP_OBJ_NULL; + } + o->base.type = &mp_type_code; + o->rc = rc; + o->dict_locals = mp_locals_get(); // this is a wrong! how to do this properly? + o->lnotab = MP_OBJ_NULL; + return MP_OBJ_FROM_PTR(o); +} + +/******************************************************************************/ +// frame object + +STATIC void frame_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_frame_t *frame = MP_OBJ_TO_PTR(o_in); + mp_obj_code_t *code = frame->code; + const mp_raw_code_t *rc = code->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + mp_printf(print, + "", + frame, + prelude->qstr_source_file, + frame->lineno, + prelude->qstr_block_name + ); +} + +STATIC void frame_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + + mp_obj_frame_t *o = MP_OBJ_TO_PTR(self_in); + + switch(attr) { + case MP_QSTR_f_back: + dest[0] = mp_const_none; + if (o->code_state->prev_state) { + dest[0] = MP_OBJ_FROM_PTR(o->code_state->prev_state->frame); + } + break; + case MP_QSTR_f_code: + dest[0] = MP_OBJ_FROM_PTR(o->code); + break; + case MP_QSTR_f_globals: + dest[0] = MP_OBJ_FROM_PTR(o->code_state->fun_bc->globals); + break; + case MP_QSTR_f_lasti: + dest[0] = MP_OBJ_NEW_SMALL_INT(o->lasti); + break; + case MP_QSTR_f_lineno: + dest[0] = MP_OBJ_NEW_SMALL_INT(o->lineno); + break; + } +} + +const mp_obj_type_t mp_type_frame = { + { &mp_type_type }, + .name = MP_QSTR_frame, + .print = frame_print, + .unary_op = mp_generic_unary_op, + .attr = frame_attr, +}; + +mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state) { + if (gc_is_locked()) { + return MP_OBJ_NULL; + } + + mp_obj_frame_t *o = m_new_obj_maybe(mp_obj_frame_t); + if (o == NULL) { + return MP_OBJ_NULL; + } + + mp_obj_code_t *code = o->code = MP_OBJ_TO_PTR(mp_obj_new_code(code_state->fun_bc->rc)); + if (code == NULL) { + return MP_OBJ_NULL; + } + + const mp_raw_code_t *rc = code->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + o->code_state = code_state; + o->base.type = &mp_type_frame; + o->back = NULL; + o->code = code; + o->lasti = code_state->ip - prelude->opcodes; + o->lineno = mp_prof_bytecode_lineno(rc, o->lasti); + o->trace_opcodes = false; + o->callback = MP_OBJ_NULL; + + return MP_OBJ_FROM_PTR(o); +} + + +/******************************************************************************/ +// Trace logic + +typedef struct { + struct _mp_obj_frame_t * frame; + mp_obj_t event; + mp_obj_t arg; +} prof_callback_args_t; + +STATIC mp_obj_t mp_prof_callback_invoke(mp_obj_t callback, prof_callback_args_t *args) { + assert(mp_obj_is_callable(callback)); + + mp_prof_is_executing = true; + + mp_obj_t a[3] = {MP_OBJ_FROM_PTR(args->frame), args->event, args->arg}; + mp_obj_t top = mp_call_function_n_kw(callback, 3, 0, a); + + mp_prof_is_executing = false; + + if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + mp_obj_t obj = MP_STATE_VM(mp_pending_exception); + MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + nlr_raise(obj); + } + return top; +} + +mp_obj_t mp_prof_settrace(mp_obj_t callback) { + if (mp_obj_is_callable(callback)) { + prof_trace_cb = callback; + } else { + prof_trace_cb = MP_OBJ_NULL; + } + return mp_const_none; +} + +mp_obj_t mp_prof_frame_enter(mp_code_state_t *code_state) { + assert(!mp_prof_is_executing); + + mp_obj_frame_t *frame = MP_OBJ_TO_PTR(mp_obj_new_frame(code_state)); + if (frame == NULL) { + // Couldn't allocate a frame object + return MP_OBJ_NULL; + } + + if (code_state->prev_state && code_state->frame == NULL) { + // We are entering not-yet-traced frame + // which means it's a CALL event (not a GENERATOR) + // so set the function definition line. + const mp_raw_code_t *rc = code_state->fun_bc->rc; + frame->lineno = rc->line_of_definition; + if (!rc->line_of_definition) { + frame->lineno = mp_prof_bytecode_lineno(rc, 0); + } + } + code_state->frame = frame; + + if (!prof_trace_cb) { + return MP_OBJ_NULL; + } + + mp_obj_t top; + prof_callback_args_t _args, *args=&_args; + args->frame = code_state->frame; + + // SETTRACE event CALL + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_call); + args->arg = mp_const_none; + top = mp_prof_callback_invoke(prof_trace_cb, args); + + code_state->frame->callback = mp_obj_is_callable(top) ? top : MP_OBJ_NULL; + + // Invalidate the last executed line number so the LINE trace can trigger after this CALL. + frame->lineno = 0; + + return top; +} + +mp_obj_t mp_prof_frame_update(const mp_code_state_t *code_state) { + mp_obj_frame_t *frame = code_state->frame; + if (frame == NULL) { + // Frame was not allocated (eg because there was no memory available) + return MP_OBJ_NULL; + } + + mp_obj_frame_t *o = frame; + mp_obj_code_t *code = o->code; + const mp_raw_code_t *rc = code->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + + assert(o->code_state == code_state); + + o->lasti = code_state->ip - prelude->opcodes; + o->lineno = mp_prof_bytecode_lineno(rc, o->lasti); + + return MP_OBJ_FROM_PTR(o); +} + +mp_obj_t mp_prof_instr_tick(mp_code_state_t *code_state, bool is_exception) { + // Detect execution recursion + assert(!mp_prof_is_executing); + assert(code_state->frame); + assert(mp_obj_get_type(code_state->frame) == &mp_type_frame); + + // Detect data recursion + assert(code_state != code_state->prev_state); + + mp_obj_t top = mp_const_none; + mp_obj_t callback = code_state->frame->callback; + + prof_callback_args_t _args, *args=&_args; + args->frame = code_state->frame; + args->event = mp_const_none; + args->arg = mp_const_none; + + // Call event's are handled inside mp_prof_frame_enter + + // SETTRACE event EXCEPTION + if (is_exception) { + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_exception); + top = mp_prof_callback_invoke(callback, args); + return top; + } + + // SETTRACE event LINE + const mp_raw_code_t *rc = code_state->fun_bc->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + size_t prev_line_no = args->frame->lineno; + size_t current_line_no = mp_prof_bytecode_lineno(rc, code_state->ip - prelude->opcodes); + if (prev_line_no != current_line_no) { + args->frame->lineno = current_line_no; + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_line); + top = mp_prof_callback_invoke(callback, args); + } + + // SETTRACE event RETURN + const byte *ip = code_state->ip; + if (*ip == MP_BC_RETURN_VALUE || *ip == MP_BC_YIELD_VALUE) { + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_return); + top = mp_prof_callback_invoke(callback, args); + if (code_state->prev_state && *ip == MP_BC_RETURN_VALUE) { + code_state->frame->callback = MP_OBJ_NULL; + } + } + + // SETTRACE event OPCODE + // TODO: frame.f_trace_opcodes=True + if (false) { + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_opcode); + } + + return top; +} + +/******************************************************************************/ +// DEBUG + +// This section is for debugging the settrace feature itself, and is not intended +// to be included in production/release builds. The code structure for this block +// was taken from py/showbc.c and should not be used as a reference. To enable +// this debug feature enable MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE in py/profile.h. +#if MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE + +#include "runtime0.h" + +#define DECODE_UINT { \ + unum = 0; \ + do { \ + unum = (unum << 7) + (*ip & 0x7f); \ + } while ((*ip++ & 0x80) != 0); \ +} +#define DECODE_ULABEL do { unum = (ip[0] | (ip[1] << 8)); ip += 2; } while (0) +#define DECODE_SLABEL do { unum = (ip[0] | (ip[1] << 8)) - 0x8000; ip += 2; } while (0) + +#define DECODE_QSTR \ + qst = ip[0] | ip[1] << 8; \ + ip += 2; +#define DECODE_PTR \ + DECODE_UINT; \ + ptr = (const byte*)const_table[unum] +#define DECODE_OBJ \ + DECODE_UINT; \ + obj = (mp_obj_t)const_table[unum] + +typedef struct _mp_dis_instruction_t { + mp_uint_t qstr_opname; + mp_uint_t arg; + mp_obj_t argobj; + mp_obj_t argobjex_cache; +} mp_dis_instruction_t; + +STATIC const byte *mp_prof_opcode_decode(const byte *ip, const mp_uint_t *const_table, mp_dis_instruction_t *instruction) { + mp_uint_t unum; + const byte* ptr; + mp_obj_t obj; + qstr qst; + + instruction->qstr_opname = MP_QSTR_; + instruction->arg = 0; + instruction->argobj= mp_const_none; + instruction->argobjex_cache = mp_const_none; + + switch (*ip++) { + case MP_BC_LOAD_CONST_FALSE: + instruction->qstr_opname = MP_QSTR_LOAD_CONST_FALSE; + break; + + case MP_BC_LOAD_CONST_NONE: + instruction->qstr_opname = MP_QSTR_LOAD_CONST_NONE; + break; + + case MP_BC_LOAD_CONST_TRUE: + instruction->qstr_opname = MP_QSTR_LOAD_CONST_TRUE; + break; + + case MP_BC_LOAD_CONST_SMALL_INT: { + mp_int_t num = 0; + if ((ip[0] & 0x40) != 0) { + // Number is negative + num--; + } + do { + num = (num << 7) | (*ip & 0x7f); + } while ((*ip++ & 0x80) != 0); + instruction->qstr_opname = MP_QSTR_LOAD_CONST_SMALL_INT; + instruction->arg = num; + break; + } + + case MP_BC_LOAD_CONST_STRING: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_CONST_STRING; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_LOAD_CONST_OBJ: + DECODE_OBJ; + instruction->qstr_opname = MP_QSTR_LOAD_CONST_OBJ; + instruction->arg = unum; + instruction->argobj= obj; + break; + + case MP_BC_LOAD_NULL: + instruction->qstr_opname = MP_QSTR_LOAD_NULL; + break; + + case MP_BC_LOAD_FAST_N: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_LOAD_FAST_N; + instruction->arg = unum; + break; + + case MP_BC_LOAD_DEREF: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_LOAD_DEREF; + instruction->arg = unum; + break; + + case MP_BC_LOAD_NAME: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_NAME; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(*ip++); + } + break; + + case MP_BC_LOAD_GLOBAL: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_GLOBAL; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(*ip++); + } + break; + + case MP_BC_LOAD_ATTR: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_ATTR; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(*ip++); + } + break; + + case MP_BC_LOAD_METHOD: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_METHOD; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_LOAD_SUPER_METHOD: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_SUPER_METHOD; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_LOAD_BUILD_CLASS: + instruction->qstr_opname = MP_QSTR_LOAD_BUILD_CLASS; + break; + + case MP_BC_LOAD_SUBSCR: + instruction->qstr_opname = MP_QSTR_LOAD_SUBSCR; + break; + + case MP_BC_STORE_FAST_N: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_STORE_FAST_N; + instruction->arg = unum; + break; + + case MP_BC_STORE_DEREF: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_STORE_DEREF; + instruction->arg = unum; + break; + + case MP_BC_STORE_NAME: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_STORE_NAME; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_STORE_GLOBAL: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_STORE_GLOBAL; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_STORE_ATTR: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_STORE_ATTR; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(*ip++); + } + break; + + case MP_BC_STORE_SUBSCR: + instruction->qstr_opname = MP_QSTR_STORE_SUBSCR; + break; + + case MP_BC_DELETE_FAST: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_DELETE_FAST; + instruction->arg = unum; + break; + + case MP_BC_DELETE_DEREF: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_DELETE_DEREF; + instruction->arg = unum; + break; + + case MP_BC_DELETE_NAME: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_DELETE_NAME; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_DELETE_GLOBAL: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_DELETE_GLOBAL; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_DUP_TOP: + instruction->qstr_opname = MP_QSTR_DUP_TOP; + break; + + case MP_BC_DUP_TOP_TWO: + instruction->qstr_opname = MP_QSTR_DUP_TOP_TWO; + break; + + case MP_BC_POP_TOP: + instruction->qstr_opname = MP_QSTR_POP_TOP; + break; + + case MP_BC_ROT_TWO: + instruction->qstr_opname = MP_QSTR_ROT_TWO; + break; + + case MP_BC_ROT_THREE: + instruction->qstr_opname = MP_QSTR_ROT_THREE; + break; + + case MP_BC_JUMP: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_JUMP; + instruction->arg = unum; + break; + + case MP_BC_POP_JUMP_IF_TRUE: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_POP_JUMP_IF_TRUE; + instruction->arg = unum; + break; + + case MP_BC_POP_JUMP_IF_FALSE: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_POP_JUMP_IF_FALSE; + instruction->arg = unum; + break; + + case MP_BC_JUMP_IF_TRUE_OR_POP: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_JUMP_IF_TRUE_OR_POP; + instruction->arg = unum; + break; + + case MP_BC_JUMP_IF_FALSE_OR_POP: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_JUMP_IF_FALSE_OR_POP; + instruction->arg = unum; + break; + + case MP_BC_SETUP_WITH: + DECODE_ULABEL; // loop-like labels are always forward + instruction->qstr_opname = MP_QSTR_SETUP_WITH; + instruction->arg = unum; + break; + + case MP_BC_WITH_CLEANUP: + instruction->qstr_opname = MP_QSTR_WITH_CLEANUP; + break; + + case MP_BC_UNWIND_JUMP: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_UNWIND_JUMP; + instruction->arg = unum; + break; + + case MP_BC_SETUP_EXCEPT: + DECODE_ULABEL; // except labels are always forward + instruction->qstr_opname = MP_QSTR_SETUP_EXCEPT; + instruction->arg = unum; + break; + + case MP_BC_SETUP_FINALLY: + DECODE_ULABEL; // except labels are always forward + instruction->qstr_opname = MP_QSTR_SETUP_FINALLY; + instruction->arg = unum; + break; + + case MP_BC_END_FINALLY: + // if TOS is an exception, reraises the exception (3 values on TOS) + // if TOS is an integer, does something else + // if TOS is None, just pops it and continues + // else error + instruction->qstr_opname = MP_QSTR_END_FINALLY; + break; + + case MP_BC_GET_ITER: + instruction->qstr_opname = MP_QSTR_GET_ITER; + break; + + case MP_BC_GET_ITER_STACK: + instruction->qstr_opname = MP_QSTR_GET_ITER_STACK; + break; + + case MP_BC_FOR_ITER: + DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward + instruction->qstr_opname = MP_QSTR_FOR_ITER; + instruction->arg = unum; + break; + + case MP_BC_BUILD_TUPLE: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_TUPLE; + instruction->arg = unum; + break; + + case MP_BC_BUILD_LIST: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_LIST; + instruction->arg = unum; + break; + + case MP_BC_BUILD_MAP: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_MAP; + instruction->arg = unum; + break; + + case MP_BC_STORE_MAP: + instruction->qstr_opname = MP_QSTR_STORE_MAP; + break; + + case MP_BC_BUILD_SET: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_SET; + instruction->arg = unum; + break; + + #if MICROPY_PY_BUILTINS_SLICE + case MP_BC_BUILD_SLICE: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_SLICE; + instruction->arg = unum; + break; + #endif + + case MP_BC_STORE_COMP: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_STORE_COMP; + instruction->arg = unum; + break; + + case MP_BC_UNPACK_SEQUENCE: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_UNPACK_SEQUENCE; + instruction->arg = unum; + break; + + case MP_BC_UNPACK_EX: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_UNPACK_EX; + instruction->arg = unum; + break; + + case MP_BC_MAKE_FUNCTION: + DECODE_PTR; + instruction->qstr_opname = MP_QSTR_MAKE_FUNCTION; + instruction->arg = unum; + instruction->argobj= mp_obj_new_int_from_ull((uint64_t)ptr); + break; + + case MP_BC_MAKE_FUNCTION_DEFARGS: + DECODE_PTR; + instruction->qstr_opname = MP_QSTR_MAKE_FUNCTION_DEFARGS; + instruction->arg = unum; + instruction->argobj= mp_obj_new_int_from_ull((uint64_t)ptr); + break; + + case MP_BC_MAKE_CLOSURE: { + DECODE_PTR; + mp_uint_t n_closed_over = *ip++; + instruction->qstr_opname = MP_QSTR_MAKE_CLOSURE; + instruction->arg = unum; + instruction->argobj= mp_obj_new_int_from_ull((uint64_t)ptr); + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(n_closed_over); + break; + } + + case MP_BC_MAKE_CLOSURE_DEFARGS: { + DECODE_PTR; + mp_uint_t n_closed_over = *ip++; + instruction->qstr_opname = MP_QSTR_MAKE_CLOSURE_DEFARGS; + instruction->arg = unum; + instruction->argobj= mp_obj_new_int_from_ull((uint64_t)ptr); + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(n_closed_over); + break; + } + + case MP_BC_CALL_FUNCTION: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_CALL_FUNCTION; + instruction->arg = unum & 0xff; + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff); + break; + + case MP_BC_CALL_FUNCTION_VAR_KW: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_CALL_FUNCTION_VAR_KW; + instruction->arg = unum & 0xff; + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff); + break; + + case MP_BC_CALL_METHOD: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_CALL_METHOD; + instruction->arg = unum & 0xff; + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff); + break; + + case MP_BC_CALL_METHOD_VAR_KW: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_CALL_METHOD_VAR_KW; + instruction->arg = unum & 0xff; + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff); + break; + + case MP_BC_RETURN_VALUE: + instruction->qstr_opname = MP_QSTR_RETURN_VALUE; + break; + + case MP_BC_RAISE_LAST: + instruction->qstr_opname = MP_QSTR_RAISE_LAST; + break; + + case MP_BC_RAISE_OBJ: + instruction->qstr_opname = MP_QSTR_RAISE_OBJ; + break; + + case MP_BC_RAISE_FROM: + instruction->qstr_opname = MP_QSTR_RAISE_FROM; + break; + + case MP_BC_YIELD_VALUE: + instruction->qstr_opname = MP_QSTR_YIELD_VALUE; + break; + + case MP_BC_YIELD_FROM: + instruction->qstr_opname = MP_QSTR_YIELD_FROM; + break; + + case MP_BC_IMPORT_NAME: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_IMPORT_NAME; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_IMPORT_FROM: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_IMPORT_FROM; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_IMPORT_STAR: + instruction->qstr_opname = MP_QSTR_IMPORT_STAR; + break; + + default: + if (ip[-1] < MP_BC_LOAD_CONST_SMALL_INT_MULTI + 64) { + instruction->qstr_opname = MP_QSTR_LOAD_CONST_SMALL_INT; + instruction->arg = (mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - 16; + } else if (ip[-1] < MP_BC_LOAD_FAST_MULTI + 16) { + instruction->qstr_opname = MP_QSTR_LOAD_FAST; + instruction->arg = (mp_uint_t)ip[-1] - MP_BC_LOAD_FAST_MULTI; + } else if (ip[-1] < MP_BC_STORE_FAST_MULTI + 16) { + instruction->qstr_opname = MP_QSTR_STORE_FAST; + instruction->arg = (mp_uint_t)ip[-1] - MP_BC_STORE_FAST_MULTI; + } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NUM_BYTECODE) { + instruction->qstr_opname = MP_QSTR_UNARY_OP; + instruction->arg = (mp_uint_t)ip[-1] - MP_BC_UNARY_OP_MULTI; + } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + MP_BINARY_OP_NUM_BYTECODE) { + mp_uint_t op = ip[-1] - MP_BC_BINARY_OP_MULTI; + instruction->qstr_opname = MP_QSTR_BINARY_OP; + instruction->arg = op; + } else { + mp_printf(&mp_plat_print, "code %p, opcode 0x%02x not implemented\n", ip-1, ip[-1]); + assert(0); + return ip; + } + break; + } + + return ip; +} + +void mp_prof_print_instr(const byte* ip, mp_code_state_t *code_state) { + mp_dis_instruction_t _instruction, *instruction = &_instruction; + mp_prof_opcode_decode(ip, code_state->fun_bc->rc->const_table, instruction); + const mp_raw_code_t *rc = code_state->fun_bc->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + + mp_uint_t offset = ip - prelude->opcodes; + mp_printf(&mp_plat_print, "instr"); + + /* long path */ if (1) { + mp_printf(&mp_plat_print, + "@0x%p:%q:%q+0x%04x:%d", + ip, + prelude->qstr_source_file, + prelude->qstr_block_name, + offset, + mp_prof_bytecode_lineno(rc, offset) + ); + } + + /* bytecode */ if (0) { + mp_printf(&mp_plat_print, " %02x %02x %02x %02x", ip[0], ip[1], ip[2], ip[3]); + } + + mp_printf(&mp_plat_print, " 0x%02x %q [%d]", *ip, instruction->qstr_opname, instruction->arg); + + if (instruction->argobj != mp_const_none) { + mp_printf(&mp_plat_print, " $"); + mp_obj_print_helper(&mp_plat_print, instruction->argobj, PRINT_REPR); + } + if (instruction->argobjex_cache != mp_const_none) { + mp_printf(&mp_plat_print, " #"); + mp_obj_print_helper(&mp_plat_print, instruction->argobjex_cache, PRINT_REPR); + } + + mp_printf(&mp_plat_print, "\n"); +} + +#endif // MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE + +#endif // MICROPY_PY_SYS_SETTRACE diff --git a/python/src/py/profile.h b/python/src/py/profile.h new file mode 100644 index 000000000..0293e262f --- /dev/null +++ b/python/src/py/profile.h @@ -0,0 +1,79 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT HOLDERS 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. + */ + +#ifndef MICROPY_INCLUDED_PY_PROFILING_H +#define MICROPY_INCLUDED_PY_PROFILING_H + +#include "py/emitglue.h" + +#if MICROPY_PY_SYS_SETTRACE + +#define mp_prof_is_executing MP_STATE_THREAD(prof_callback_is_executing) + +typedef struct _mp_obj_code_t { + mp_obj_base_t base; + const mp_raw_code_t *rc; + mp_obj_dict_t *dict_locals; + mp_obj_t lnotab; +} mp_obj_code_t; + +typedef struct _mp_obj_frame_t { + mp_obj_base_t base; + const mp_code_state_t *code_state; + struct _mp_obj_frame_t *back; + mp_obj_t callback; + mp_obj_code_t *code; + mp_uint_t lasti; + mp_uint_t lineno; + bool trace_opcodes; +} mp_obj_frame_t; + +void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelude); + +mp_obj_t mp_obj_new_code(const mp_raw_code_t *rc); +mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state); + +// This is the implementation for the sys.settrace +mp_obj_t mp_prof_settrace(mp_obj_t callback); + +mp_obj_t mp_prof_frame_enter(mp_code_state_t *code_state); +mp_obj_t mp_prof_frame_update(const mp_code_state_t *code_state); + +// For every VM instruction tick this function deduces events from the state +mp_obj_t mp_prof_instr_tick(mp_code_state_t *code_state, bool is_exception); + +// This section is for debugging the settrace feature itself, and is not intended +// to be included in production/release builds. +#define MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE 0 +#if MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE +void mp_prof_print_instr(const byte* ip, mp_code_state_t *code_state); +#define MP_PROF_INSTR_DEBUG_PRINT(current_ip) mp_prof_print_instr((current_ip), code_state) +#else +#define MP_PROF_INSTR_DEBUG_PRINT(current_ip) +#endif + +#endif // MICROPY_PY_SYS_SETTRACE +#endif // MICROPY_INCLUDED_PY_PROFILING_H diff --git a/python/src/py/qstr.h b/python/src/py/qstr.h index f4375ee0e..8153ef29f 100644 --- a/python/src/py/qstr.h +++ b/python/src/py/qstr.h @@ -35,7 +35,7 @@ // Note: it would be possible to define MP_QSTR_xxx as qstr_from_str_static("xxx") // for qstrs that are referenced this way, but you don't want to have them in ROM. -// first entry in enum will be MP_QSTR_NULL=0, which indicates invalid/no qstr +// first entry in enum will be MP_QSTRnull=0, which indicates invalid/no qstr enum { #ifndef NO_QSTR #define QDEF(id, str) id, @@ -61,7 +61,7 @@ typedef struct _qstr_pool_t { void qstr_init(void); mp_uint_t qstr_compute_hash(const byte *data, size_t len); -qstr qstr_find_strn(const char *str, size_t str_len); // returns MP_QSTR_NULL if not found +qstr qstr_find_strn(const char *str, size_t str_len); // returns MP_QSTRnull if not found qstr qstr_from_str(const char *str); qstr qstr_from_strn(const char *str, size_t len); diff --git a/python/src/py/repl.c b/python/src/py/repl.c index da0fefb3a..9389b3424 100644 --- a/python/src/py/repl.c +++ b/python/src/py/repl.c @@ -159,7 +159,7 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print if (str < top) { // a complete word, lookup in current object qstr q = qstr_find_strn(s_start, s_len); - if (q == MP_QSTR_NULL) { + if (q == MP_QSTRnull) { // lookup will fail return 0; } diff --git a/python/src/py/ringbuf.c b/python/src/py/ringbuf.c new file mode 100644 index 000000000..83887b300 --- /dev/null +++ b/python/src/py/ringbuf.c @@ -0,0 +1,73 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jim Mussared + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT HOLDERS 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 "ringbuf.h" + +int ringbuf_get16(ringbuf_t *r) { + int v = ringbuf_peek16(r); + if (v == -1) { + return v; + } + r->iget += 2; + if (r->iget >= r->size) { + r->iget -= r->size; + } + return v; +} + +int ringbuf_peek16(ringbuf_t *r) { + if (r->iget == r->iput) { + return -1; + } + uint32_t iget_a = r->iget + 1; + if (iget_a == r->size) { + iget_a = 0; + } + if (iget_a == r->iput) { + return -1; + } + return (r->buf[r->iget] << 8) | (r->buf[iget_a]); +} + +int ringbuf_put16(ringbuf_t *r, uint16_t v) { + uint32_t iput_a = r->iput + 1; + if (iput_a == r->size) { + iput_a = 0; + } + if (iput_a == r->iget) { + return -1; + } + uint32_t iput_b = iput_a + 1; + if (iput_b == r->size) { + iput_b = 0; + } + if (iput_b == r->iget) { + return -1; + } + r->buf[r->iput] = (v >> 8) & 0xff; + r->buf[iput_a] = v & 0xff; + r->iput = iput_b; + return 0; +} diff --git a/python/src/py/ringbuf.h b/python/src/py/ringbuf.h index b41692706..8d4ed1643 100644 --- a/python/src/py/ringbuf.h +++ b/python/src/py/ringbuf.h @@ -26,6 +26,9 @@ #ifndef MICROPY_INCLUDED_PY_RINGBUF_H #define MICROPY_INCLUDED_PY_RINGBUF_H +#include +#include + typedef struct _ringbuf_t { uint8_t *buf; uint16_t size; @@ -37,7 +40,7 @@ typedef struct _ringbuf_t { // byte buf_array[N]; // ringbuf_t buf = {buf_array, sizeof(buf_array)}; -// Dynamic initialization. This creates root pointer! +// Dynamic initialization. This needs to become findable as a root pointer! #define ringbuf_alloc(r, sz) \ { \ (r)->buf = m_new(uint8_t, sz); \ @@ -69,4 +72,17 @@ static inline int ringbuf_put(ringbuf_t *r, uint8_t v) { return 0; } +static inline size_t ringbuf_free(ringbuf_t *r) { + return (r->size + r->iget - r->iput - 1) % r->size; +} + +static inline size_t ringbuf_avail(ringbuf_t *r) { + return (r->size + r->iput - r->iget) % r->size; +} + +// Note: big-endian. No-op if not enough room available for both bytes. +int ringbuf_get16(ringbuf_t *r); +int ringbuf_peek16(ringbuf_t *r); +int ringbuf_put16(ringbuf_t *r, uint16_t v); + #endif // MICROPY_INCLUDED_PY_RINGBUF_H diff --git a/python/src/py/runtime.c b/python/src/py/runtime.c index e50256605..deb82e935 100644 --- a/python/src/py/runtime.c +++ b/python/src/py/runtime.c @@ -88,6 +88,9 @@ void mp_init(void) { #if MICROPY_ENABLE_COMPILER // optimization disabled by default MP_STATE_VM(mp_optimise_value) = 0; + #if MICROPY_EMIT_NATIVE + MP_STATE_VM(default_emit_opt) = MP_EMIT_OPT_NONE; + #endif #endif // init global module dict @@ -118,6 +121,20 @@ void mp_init(void) { MP_STATE_VM(vfs_mount_table) = NULL; #endif + #if MICROPY_PY_SYS_ATEXIT + MP_STATE_VM(sys_exitfunc) = mp_const_none; + #endif + + #if MICROPY_PY_SYS_SETTRACE + MP_STATE_THREAD(prof_trace_callback) = MP_OBJ_NULL; + MP_STATE_THREAD(prof_callback_is_executing) = false; + MP_STATE_THREAD(current_code_state) = NULL; + #endif + + #if MICROPY_PY_BLUETOOTH + MP_STATE_VM(bluetooth) = MP_OBJ_NULL; + #endif + #if MICROPY_PY_THREAD_GIL mp_thread_mutex_init(&MP_STATE_VM(gil_mutex)); #endif @@ -553,16 +570,17 @@ generic_binary_op: } #if MICROPY_PY_REVERSE_SPECIAL_METHODS - if (op >= MP_BINARY_OP_OR && op <= MP_BINARY_OP_REVERSE_POWER) { + if (op >= MP_BINARY_OP_OR && op <= MP_BINARY_OP_POWER) { mp_obj_t t = rhs; rhs = lhs; lhs = t; - if (op <= MP_BINARY_OP_POWER) { - op += MP_BINARY_OP_REVERSE_OR - MP_BINARY_OP_OR; - goto generic_binary_op; - } - + op += MP_BINARY_OP_REVERSE_OR - MP_BINARY_OP_OR; + goto generic_binary_op; + } else if (op >= MP_BINARY_OP_REVERSE_OR) { // Convert __rop__ back to __op__ for error message + mp_obj_t t = rhs; + rhs = lhs; + lhs = t; op -= MP_BINARY_OP_REVERSE_OR - MP_BINARY_OP_OR; } #endif @@ -1302,7 +1320,12 @@ mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t th // will be propagated up. This behavior is approved by test_pep380.py // test_delegation_of_close_to_non_generator(), // test_delegating_throw_to_non_generator() - *ret_val = mp_make_raise_obj(throw_value); + if (mp_obj_exception_match(throw_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))) { + // PEP479: if StopIteration is raised inside a generator it is replaced with RuntimeError + *ret_val = mp_obj_new_exception_msg(&mp_type_RuntimeError, "generator raised StopIteration"); + } else { + *ret_val = mp_make_raise_obj(throw_value); + } return MP_VM_RETURN_EXCEPTION; } } @@ -1335,7 +1358,17 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) { args[3] = fromlist; args[4] = level; - // TODO lookup __import__ and call that instead of going straight to builtin implementation + #if MICROPY_CAN_OVERRIDE_BUILTINS + // Lookup __import__ and call that if it exists + mp_obj_dict_t *bo_dict = MP_STATE_VM(mp_module_builtins_override_dict); + if (bo_dict != NULL) { + mp_map_elem_t *import = mp_map_lookup(&bo_dict->map, MP_OBJ_NEW_QSTR(MP_QSTR___import__), MP_MAP_LOOKUP); + if (import != NULL) { + return mp_call_function_n_kw(import->value, 5, 0, args); + } + } + #endif + return mp_builtin___import__(5, args); } @@ -1407,7 +1440,6 @@ void mp_import_all(mp_obj_t module) { #if MICROPY_ENABLE_COMPILER -// this is implemented in this file so it can optimise access to locals/globals mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_input_kind, mp_obj_dict_t *globals, mp_obj_dict_t *locals) { // save context mp_obj_dict_t *volatile old_globals = mp_globals_get(); @@ -1421,7 +1453,7 @@ mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_i if (nlr_push(&nlr) == 0) { qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, parse_input_kind); - mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, false); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, false); mp_obj_t ret; if (MICROPY_PY_BUILTINS_COMPILE && globals == NULL) { diff --git a/python/src/py/runtime.h b/python/src/py/runtime.h index 0eb15d461..b54b17b68 100644 --- a/python/src/py/runtime.h +++ b/python/src/py/runtime.h @@ -151,7 +151,6 @@ mp_obj_t mp_import_from(mp_obj_t module, qstr name); void mp_import_all(mp_obj_t module); NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, const char *msg); -//NORETURN void nlr_raise_msg_varg(const mp_obj_type_t *exc_type, const char *fmt, ...); NORETURN void mp_raise_ValueError(const char *msg); NORETURN void mp_raise_TypeError(const char *msg); NORETURN void mp_raise_NotImplementedError(const char *msg); @@ -172,9 +171,6 @@ NORETURN void mp_raise_recursion_depth(void); int mp_native_type_from_qstr(qstr qst); mp_uint_t mp_native_from_obj(mp_obj_t obj, mp_uint_t type); mp_obj_t mp_native_to_obj(mp_uint_t val, mp_uint_t type); -mp_obj_dict_t *mp_native_swap_globals(mp_obj_dict_t *new_globals); -mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, size_t n_args_kw, const mp_obj_t *args); -void mp_native_raise(mp_obj_t o); #define mp_sys_path (MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_sys_path_obj))) #define mp_sys_argv (MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_sys_argv_obj))) diff --git a/python/src/py/runtime0.h b/python/src/py/runtime0.h index efd439196..e6eeff97d 100644 --- a/python/src/py/runtime0.h +++ b/python/src/py/runtime0.h @@ -28,13 +28,17 @@ // The first four must fit in 8 bits, see emitbc.c // The remaining must fit in 16 bits, see scope.h -#define MP_SCOPE_FLAG_VARARGS (0x01) +#define MP_SCOPE_FLAG_ALL_SIG (0x0f) +#define MP_SCOPE_FLAG_GENERATOR (0x01) #define MP_SCOPE_FLAG_VARKEYWORDS (0x02) -#define MP_SCOPE_FLAG_GENERATOR (0x04) +#define MP_SCOPE_FLAG_VARARGS (0x04) #define MP_SCOPE_FLAG_DEFKWARGS (0x08) #define MP_SCOPE_FLAG_REFGLOBALS (0x10) // used only if native emitter enabled #define MP_SCOPE_FLAG_HASCONSTS (0x20) // used only if native emitter enabled -#define MP_SCOPE_FLAG_VIPERRET_POS (6) // 3 bits used for viper return type +#define MP_SCOPE_FLAG_VIPERRET_POS (6) // 3 bits used for viper return type, to pass from compiler to native emitter +#define MP_SCOPE_FLAG_VIPERRELOC (0x10) // used only when loading viper from .mpy +#define MP_SCOPE_FLAG_VIPERRODATA (0x20) // used only when loading viper from .mpy +#define MP_SCOPE_FLAG_VIPERBSS (0x40) // used only when loading viper from .mpy // types for native (viper) function signature #define MP_NATIVE_TYPE_OBJ (0x00) @@ -46,6 +50,18 @@ #define MP_NATIVE_TYPE_PTR16 (0x06) #define MP_NATIVE_TYPE_PTR32 (0x07) +// Bytecode and runtime boundaries for unary ops +#define MP_UNARY_OP_NUM_BYTECODE (MP_UNARY_OP_NOT + 1) +#define MP_UNARY_OP_NUM_RUNTIME (MP_UNARY_OP_SIZEOF + 1) + +// Bytecode and runtime boundaries for binary ops +#define MP_BINARY_OP_NUM_BYTECODE (MP_BINARY_OP_POWER + 1) +#if MICROPY_PY_REVERSE_SPECIAL_METHODS +#define MP_BINARY_OP_NUM_RUNTIME (MP_BINARY_OP_REVERSE_POWER + 1) +#else +#define MP_BINARY_OP_NUM_RUNTIME (MP_BINARY_OP_CONTAINS + 1) +#endif + typedef enum { // These ops may appear in the bytecode. Changing this group // in any way requires changing the bytecode version. @@ -55,22 +71,19 @@ typedef enum { MP_UNARY_OP_NOT, // Following ops cannot appear in the bytecode - MP_UNARY_OP_NUM_BYTECODE, - - MP_UNARY_OP_BOOL = MP_UNARY_OP_NUM_BYTECODE, // __bool__ + MP_UNARY_OP_BOOL, // __bool__ MP_UNARY_OP_LEN, // __len__ MP_UNARY_OP_HASH, // __hash__; must return a small int MP_UNARY_OP_ABS, // __abs__ MP_UNARY_OP_INT, // __int__ MP_UNARY_OP_SIZEOF, // for sys.getsizeof() - - MP_UNARY_OP_NUM_RUNTIME, } mp_unary_op_t; -// Note: the first 9+12+12 of these are used in bytecode and changing -// them requires changing the bytecode version. typedef enum { - // 9 relational operations, should return a bool + // The following 9+13+13 ops are used in bytecode and changing + // them requires changing the bytecode version. + + // 9 relational operations, should return a bool; order of first 6 matches corresponding mp_token_kind_t MP_BINARY_OP_LESS, MP_BINARY_OP_MORE, MP_BINARY_OP_EQUAL, @@ -81,7 +94,7 @@ typedef enum { MP_BINARY_OP_IS, MP_BINARY_OP_EXCEPTION_MATCH, - // 12 inplace arithmetic operations + // 13 inplace arithmetic operations; order matches corresponding mp_token_kind_t MP_BINARY_OP_INPLACE_OR, MP_BINARY_OP_INPLACE_XOR, MP_BINARY_OP_INPLACE_AND, @@ -90,12 +103,13 @@ typedef enum { MP_BINARY_OP_INPLACE_ADD, MP_BINARY_OP_INPLACE_SUBTRACT, MP_BINARY_OP_INPLACE_MULTIPLY, + MP_BINARY_OP_INPLACE_MAT_MULTIPLY, MP_BINARY_OP_INPLACE_FLOOR_DIVIDE, MP_BINARY_OP_INPLACE_TRUE_DIVIDE, MP_BINARY_OP_INPLACE_MODULO, MP_BINARY_OP_INPLACE_POWER, - // 12 normal arithmetic operations + // 13 normal arithmetic operations; order matches corresponding mp_token_kind_t MP_BINARY_OP_OR, MP_BINARY_OP_XOR, MP_BINARY_OP_AND, @@ -104,6 +118,7 @@ typedef enum { MP_BINARY_OP_ADD, MP_BINARY_OP_SUBTRACT, MP_BINARY_OP_MULTIPLY, + MP_BINARY_OP_MAT_MULTIPLY, MP_BINARY_OP_FLOOR_DIVIDE, MP_BINARY_OP_TRUE_DIVIDE, MP_BINARY_OP_MODULO, @@ -111,11 +126,18 @@ typedef enum { // Operations below this line don't appear in bytecode, they // just identify special methods. - MP_BINARY_OP_NUM_BYTECODE, - // MP_BINARY_OP_REVERSE_* must follow immediately after MP_BINARY_OP_* -#if MICROPY_PY_REVERSE_SPECIAL_METHODS - MP_BINARY_OP_REVERSE_OR = MP_BINARY_OP_NUM_BYTECODE, + // This is not emitted by the compiler but is supported by the runtime. + // It must follow immediately after MP_BINARY_OP_POWER. + MP_BINARY_OP_DIVMOD, + + // The runtime will convert MP_BINARY_OP_IN to this operator with swapped args. + // A type should implement this containment operator instead of MP_BINARY_OP_IN. + MP_BINARY_OP_CONTAINS, + + // 13 MP_BINARY_OP_REVERSE_* operations must be in the same order as MP_BINARY_OP_*, + // and be the last ones supported by the runtime. + MP_BINARY_OP_REVERSE_OR, MP_BINARY_OP_REVERSE_XOR, MP_BINARY_OP_REVERSE_AND, MP_BINARY_OP_REVERSE_LSHIFT, @@ -123,88 +145,15 @@ typedef enum { MP_BINARY_OP_REVERSE_ADD, MP_BINARY_OP_REVERSE_SUBTRACT, MP_BINARY_OP_REVERSE_MULTIPLY, + MP_BINARY_OP_REVERSE_MAT_MULTIPLY, MP_BINARY_OP_REVERSE_FLOOR_DIVIDE, MP_BINARY_OP_REVERSE_TRUE_DIVIDE, MP_BINARY_OP_REVERSE_MODULO, MP_BINARY_OP_REVERSE_POWER, -#endif - - // This is not emitted by the compiler but is supported by the runtime - MP_BINARY_OP_DIVMOD - #if !MICROPY_PY_REVERSE_SPECIAL_METHODS - = MP_BINARY_OP_NUM_BYTECODE - #endif - , - - // The runtime will convert MP_BINARY_OP_IN to this operator with swapped args. - // A type should implement this containment operator instead of MP_BINARY_OP_IN. - MP_BINARY_OP_CONTAINS, - - MP_BINARY_OP_NUM_RUNTIME, // These 2 are not supported by the runtime and must be synthesised by the emitter MP_BINARY_OP_NOT_IN, MP_BINARY_OP_IS_NOT, } mp_binary_op_t; -typedef enum { - MP_F_CONST_NONE_OBJ = 0, - MP_F_CONST_FALSE_OBJ, - MP_F_CONST_TRUE_OBJ, - MP_F_CONVERT_OBJ_TO_NATIVE, - MP_F_CONVERT_NATIVE_TO_OBJ, - MP_F_NATIVE_SWAP_GLOBALS, - MP_F_LOAD_NAME, - MP_F_LOAD_GLOBAL, - MP_F_LOAD_BUILD_CLASS, - MP_F_LOAD_ATTR, - MP_F_LOAD_METHOD, - MP_F_LOAD_SUPER_METHOD, - MP_F_STORE_NAME, - MP_F_STORE_GLOBAL, - MP_F_STORE_ATTR, - MP_F_OBJ_SUBSCR, - MP_F_OBJ_IS_TRUE, - MP_F_UNARY_OP, - MP_F_BINARY_OP, - MP_F_BUILD_TUPLE, - MP_F_BUILD_LIST, - MP_F_LIST_APPEND, - MP_F_BUILD_MAP, - MP_F_STORE_MAP, -#if MICROPY_PY_BUILTINS_SET - MP_F_STORE_SET, - MP_F_BUILD_SET, -#endif - MP_F_MAKE_FUNCTION_FROM_RAW_CODE, - MP_F_NATIVE_CALL_FUNCTION_N_KW, - MP_F_CALL_METHOD_N_KW, - MP_F_CALL_METHOD_N_KW_VAR, - MP_F_NATIVE_GETITER, - MP_F_NATIVE_ITERNEXT, - MP_F_NLR_PUSH, - MP_F_NLR_POP, - MP_F_NATIVE_RAISE, - MP_F_IMPORT_NAME, - MP_F_IMPORT_FROM, - MP_F_IMPORT_ALL, -#if MICROPY_PY_BUILTINS_SLICE - MP_F_NEW_SLICE, -#endif - MP_F_UNPACK_SEQUENCE, - MP_F_UNPACK_EX, - MP_F_DELETE_NAME, - MP_F_DELETE_GLOBAL, - MP_F_NEW_CELL, - MP_F_MAKE_CLOSURE_FROM_RAW_CODE, - MP_F_ARG_CHECK_NUM_SIG, - MP_F_SETUP_CODE_STATE, - MP_F_SMALL_INT_FLOOR_DIVIDE, - MP_F_SMALL_INT_MODULO, - MP_F_NATIVE_YIELD_FROM, - MP_F_NUMBER_OF, -} mp_fun_kind_t; - -extern const void *const mp_fun_table[MP_F_NUMBER_OF]; - #endif // MICROPY_INCLUDED_PY_RUNTIME0_H diff --git a/python/src/py/scheduler.c b/python/src/py/scheduler.c index 5edff45b6..e7cbb524d 100644 --- a/python/src/py/scheduler.c +++ b/python/src/py/scheduler.c @@ -65,7 +65,7 @@ void mp_handle_pending(void) { void mp_handle_pending_tail(mp_uint_t atomic_state) { MP_STATE_VM(sched_state) = MP_SCHED_LOCKED; if (!mp_sched_empty()) { - mp_sched_item_t item = MP_STATE_VM(sched_stack)[MP_STATE_VM(sched_idx)]; + mp_sched_item_t item = MP_STATE_VM(sched_queue)[MP_STATE_VM(sched_idx)]; MP_STATE_VM(sched_idx) = IDX_MASK(MP_STATE_VM(sched_idx) + 1); --MP_STATE_VM(sched_len); MICROPY_END_ATOMIC_SECTION(atomic_state); @@ -107,11 +107,11 @@ bool mp_sched_schedule(mp_obj_t function, mp_obj_t arg) { MP_STATE_VM(sched_state) = MP_SCHED_PENDING; } uint8_t iput = IDX_MASK(MP_STATE_VM(sched_idx) + MP_STATE_VM(sched_len)++); - MP_STATE_VM(sched_stack)[iput].func = function; - MP_STATE_VM(sched_stack)[iput].arg = arg; + MP_STATE_VM(sched_queue)[iput].func = function; + MP_STATE_VM(sched_queue)[iput].arg = arg; ret = true; } else { - // schedule stack is full + // schedule queue is full ret = false; } MICROPY_END_ATOMIC_SECTION(atomic_state); diff --git a/python/src/py/sequence.c b/python/src/py/sequence.c index c66fde98f..4c19fc69e 100644 --- a/python/src/py/sequence.c +++ b/python/src/py/sequence.c @@ -165,7 +165,7 @@ bool mp_seq_cmp_bytes(mp_uint_t op, const byte *data1, size_t len1, const byte * size_t min_len = len1 < len2 ? len1 : len2; int res = memcmp(data1, data2, min_len); if (op == MP_BINARY_OP_EQUAL) { - // If we are checking for equality, here're the answer + // If we are checking for equality, here's the answer return res == 0; } if (res < 0) { diff --git a/python/src/py/showbc.c b/python/src/py/showbc.c index b9024b716..d154511dc 100644 --- a/python/src/py/showbc.c +++ b/python/src/py/showbc.c @@ -83,17 +83,10 @@ const mp_uint_t *mp_showbc_const_table; void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const mp_uint_t *const_table) { mp_showbc_code_start = ip; - // get bytecode parameters - mp_uint_t n_state = mp_decode_uint(&ip); - mp_uint_t n_exc_stack = mp_decode_uint(&ip); - /*mp_uint_t scope_flags =*/ ip++; - mp_uint_t n_pos_args = *ip++; - mp_uint_t n_kwonly_args = *ip++; - /*mp_uint_t n_def_pos_args =*/ ip++; - + // Decode prelude + MP_BC_PRELUDE_SIG_DECODE(ip); + MP_BC_PRELUDE_SIZE_DECODE(ip); const byte *code_info = ip; - mp_uint_t code_info_size = mp_decode_uint(&code_info); - ip += code_info_size; #if MICROPY_PERSISTENT_CODE qstr block_name = code_info[0] | (code_info[1] << 8); @@ -107,7 +100,9 @@ void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const m qstr_str(source_file), qstr_str(block_name), descr, mp_showbc_code_start, len); // raw bytecode dump - printf("Raw bytecode (code_info_size=" UINT_FMT ", bytecode_size=" UINT_FMT "):\n", code_info_size, len - code_info_size); + size_t prelude_size = ip - mp_showbc_code_start + n_info + n_cell; + printf("Raw bytecode (code_info_size=" UINT_FMT ", bytecode_size=" UINT_FMT "):\n", + prelude_size, len - prelude_size); for (mp_uint_t i = 0; i < len; i++) { if (i > 0 && i % 16 == 0) { printf("\n"); @@ -123,24 +118,21 @@ void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const m } printf("\n"); - printf("(N_STATE " UINT_FMT ")\n", n_state); - printf("(N_EXC_STACK " UINT_FMT ")\n", n_exc_stack); + printf("(N_STATE %u)\n", (unsigned)n_state); + printf("(N_EXC_STACK %u)\n", (unsigned)n_exc_stack); - // for printing line number info - const byte *bytecode_start = ip; + // skip over code_info + ip += n_info; // bytecode prelude: initialise closed over variables - { - uint local_num; - while ((local_num = *ip++) != 255) { - printf("(INIT_CELL %u)\n", local_num); - } - len -= ip - mp_showbc_code_start; + for (size_t i = 0; i < n_cell; ++i) { + uint local_num = *ip++; + printf("(INIT_CELL %u)\n", local_num); } // print out line number info { - mp_int_t bc = bytecode_start - ip; + mp_int_t bc = 0; mp_uint_t source_line = 1; printf(" bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); for (const byte* ci = code_info; *ci;) { @@ -158,7 +150,7 @@ void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const m printf(" bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); } } - mp_bytecode_print2(ip, len - 0, const_table); + mp_bytecode_print2(ip, len - prelude_size, const_table); } const byte *mp_bytecode_print_str(const byte *ip) { @@ -500,9 +492,16 @@ const byte *mp_bytecode_print_str(const byte *ip) { printf("RETURN_VALUE"); break; - case MP_BC_RAISE_VARARGS: - unum = *ip++; - printf("RAISE_VARARGS " UINT_FMT, unum); + case MP_BC_RAISE_LAST: + printf("RAISE_LAST"); + break; + + case MP_BC_RAISE_OBJ: + printf("RAISE_OBJ"); + break; + + case MP_BC_RAISE_FROM: + printf("RAISE_FROM"); break; case MP_BC_YIELD_VALUE: @@ -540,7 +539,7 @@ const byte *mp_bytecode_print_str(const byte *ip) { mp_uint_t op = ip[-1] - MP_BC_BINARY_OP_MULTI; printf("BINARY_OP " UINT_FMT " %s", op, qstr_str(mp_binary_op_method_name[op])); } else { - printf("code %p, byte code 0x%02x not implemented\n", ip, ip[-1]); + printf("code %p, byte code 0x%02x not implemented\n", ip - 1, ip[-1]); assert(0); return ip; } diff --git a/python/src/py/stream.h b/python/src/py/stream.h index b6019bb38..3ebd4e042 100644 --- a/python/src/py/stream.h +++ b/python/src/py/stream.h @@ -45,10 +45,11 @@ #define MP_STREAM_GET_FILENO (10) // Get fileno of underlying file // These poll ioctl values are compatible with Linux -#define MP_STREAM_POLL_RD (0x0001) -#define MP_STREAM_POLL_WR (0x0004) -#define MP_STREAM_POLL_ERR (0x0008) -#define MP_STREAM_POLL_HUP (0x0010) +#define MP_STREAM_POLL_RD (0x0001) +#define MP_STREAM_POLL_WR (0x0004) +#define MP_STREAM_POLL_ERR (0x0008) +#define MP_STREAM_POLL_HUP (0x0010) +#define MP_STREAM_POLL_NVAL (0x0020) // Argument structure for MP_STREAM_SEEK struct mp_stream_seek_t { diff --git a/python/src/py/vm.c b/python/src/py/vm.c index 260a7f38b..11dbffaff 100644 --- a/python/src/py/vm.c +++ b/python/src/py/vm.c @@ -34,6 +34,7 @@ #include "py/runtime.h" #include "py/bc0.h" #include "py/bc.h" +#include "py/profile.h" #if 0 #define TRACE(ip) printf("sp=%d ", (int)(sp - &code_state->state[0] + 1)); mp_bytecode_print2(ip, 1, code_state->fun_bc->const_table); @@ -108,6 +109,87 @@ exc_sp--; /* pop back to previous exception handler */ \ CLEAR_SYS_EXC_INFO() /* just clear sys.exc_info(), not compliant, but it shouldn't be used in 1st place */ +#define CANCEL_ACTIVE_FINALLY(sp) do { \ + if (mp_obj_is_small_int(sp[-1])) { \ + /* Stack: (..., prev_dest_ip, prev_cause, dest_ip) */ \ + /* Cancel the unwind through the previous finally, replace with current one */ \ + sp[-2] = sp[0]; \ + sp -= 2; \ + } else { \ + assert(sp[-1] == mp_const_none || mp_obj_is_exception_instance(sp[-1])); \ + /* Stack: (..., None/exception, dest_ip) */ \ + /* Silence the finally's exception value (may be None or an exception) */ \ + sp[-1] = sp[0]; \ + --sp; \ + } \ +} while (0) + +#if MICROPY_PY_SYS_SETTRACE + +#define FRAME_SETUP() do { \ + assert(code_state != code_state->prev_state); \ + MP_STATE_THREAD(current_code_state) = code_state; \ + assert(code_state != code_state->prev_state); \ +} while(0) + +#define FRAME_ENTER() do { \ + assert(code_state != code_state->prev_state); \ + code_state->prev_state = MP_STATE_THREAD(current_code_state); \ + assert(code_state != code_state->prev_state); \ + if (!mp_prof_is_executing) { \ + mp_prof_frame_enter(code_state); \ + } \ +} while(0) + +#define FRAME_LEAVE() do { \ + assert(code_state != code_state->prev_state); \ + MP_STATE_THREAD(current_code_state) = code_state->prev_state; \ + assert(code_state != code_state->prev_state); \ +} while(0) + +#define FRAME_UPDATE() do { \ + assert(MP_STATE_THREAD(current_code_state) == code_state); \ + if (!mp_prof_is_executing) { \ + code_state->frame = MP_OBJ_TO_PTR(mp_prof_frame_update(code_state)); \ + } \ +} while(0) + +#define TRACE_TICK(current_ip, current_sp, is_exception) do { \ + assert(code_state != code_state->prev_state); \ + assert(MP_STATE_THREAD(current_code_state) == code_state); \ + if (!mp_prof_is_executing && code_state->frame && MP_STATE_THREAD(prof_trace_callback)) { \ + MP_PROF_INSTR_DEBUG_PRINT(code_state->ip); \ + } \ + if (!mp_prof_is_executing && code_state->frame && code_state->frame->callback) { \ + mp_prof_instr_tick(code_state, is_exception); \ + } \ +} while(0) + +#else // MICROPY_PY_SYS_SETTRACE +#define FRAME_SETUP() +#define FRAME_ENTER() +#define FRAME_LEAVE() +#define FRAME_UPDATE() +#define TRACE_TICK(current_ip, current_sp, is_exception) +#endif // MICROPY_PY_SYS_SETTRACE + +#if MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE +static inline mp_map_elem_t *mp_map_cached_lookup(mp_map_t *map, qstr qst, uint8_t *idx_cache) { + size_t idx = *idx_cache; + mp_obj_t key = MP_OBJ_NEW_QSTR(qst); + mp_map_elem_t *elem = NULL; + if (idx < map->alloc && map->table[idx].key == key) { + elem = &map->table[idx]; + } else { + elem = mp_map_lookup(map, key, MP_MAP_LOOKUP); + if (elem != NULL) { + *idx_cache = (elem - &map->table[0]) & 0xff; + } + } + return elem; +} +#endif + // fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc) // sp points to bottom of stack which grows up // returns: @@ -128,6 +210,7 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp #define DISPATCH() do { \ TRACE(ip); \ MARK_EXC_IP_GLOBAL(); \ + TRACE_TICK(ip, sp, false); \ goto *entry_table[*ip++]; \ } while (0) #define DISPATCH_WITH_PEND_EXC_CHECK() goto pending_exception_check @@ -149,17 +232,24 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp #if MICROPY_STACKLESS run_code_state: ; #endif +FRAME_ENTER(); + +#if MICROPY_STACKLESS +run_code_state_from_return: ; +#endif +FRAME_SETUP(); + // Pointers which are constant for particular invocation of mp_execute_bytecode() mp_obj_t * /*const*/ fastn; mp_exc_stack_t * /*const*/ exc_stack; { - size_t n_state = mp_decode_uint_value(code_state->fun_bc->bytecode); + size_t n_state = code_state->n_state; fastn = &code_state->state[n_state - 1]; exc_stack = (mp_exc_stack_t*)(code_state->state + n_state); } // variables that are visible to the exception handler (declared volatile) - mp_exc_stack_t *volatile exc_sp = MP_TAGPTR_PTR(code_state->exc_sp); // stack grows up, exc_sp points to top of stack + mp_exc_stack_t *volatile exc_sp = MP_CODE_STATE_EXC_SP_IDX_TO_PTR(exc_stack, code_state->exc_sp_idx); // stack grows up, exc_sp points to top of stack #if MICROPY_PY_THREAD_GIL && MICROPY_PY_THREAD_GIL_VM_DIVISOR // This needs to be volatile and outside the VM loop so it persists across handling @@ -179,7 +269,7 @@ outer_dispatch_loop: MICROPY_VM_HOOK_INIT // If we have exception to inject, now that we finish setting up - // execution context, raise it. This works as if RAISE_VARARGS + // execution context, raise it. This works as if MP_BC_RAISE_OBJ // bytecode was executed. // Injecting exc into yield from generator is a special case, // handled by MP_BC_YIELD_FROM itself @@ -198,6 +288,7 @@ dispatch_loop: #else TRACE(ip); MARK_EXC_IP_GLOBAL(); + TRACE_TICK(ip, sp, false); switch (*ip++) { #endif @@ -274,19 +365,14 @@ dispatch_loop: ENTRY(MP_BC_LOAD_NAME): { MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; - mp_obj_t key = MP_OBJ_NEW_QSTR(qst); - mp_uint_t x = *ip; - if (x < mp_locals_get()->map.alloc && mp_locals_get()->map.table[x].key == key) { - PUSH(mp_locals_get()->map.table[x].value); + mp_map_elem_t *elem = mp_map_cached_lookup(&mp_locals_get()->map, qst, (uint8_t*)ip); + mp_obj_t obj; + if (elem != NULL) { + obj = elem->value; } else { - mp_map_elem_t *elem = mp_map_lookup(&mp_locals_get()->map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); - if (elem != NULL) { - *(byte*)ip = (elem - &mp_locals_get()->map.table[0]) & 0xff; - PUSH(elem->value); - } else { - PUSH(mp_load_name(MP_OBJ_QSTR_VALUE(key))); - } + obj = mp_load_name(qst); } + PUSH(obj); ip++; DISPATCH(); } @@ -303,19 +389,14 @@ dispatch_loop: ENTRY(MP_BC_LOAD_GLOBAL): { MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; - mp_obj_t key = MP_OBJ_NEW_QSTR(qst); - mp_uint_t x = *ip; - if (x < mp_globals_get()->map.alloc && mp_globals_get()->map.table[x].key == key) { - PUSH(mp_globals_get()->map.table[x].value); + mp_map_elem_t *elem = mp_map_cached_lookup(&mp_globals_get()->map, qst, (uint8_t*)ip); + mp_obj_t obj; + if (elem != NULL) { + obj = elem->value; } else { - mp_map_elem_t *elem = mp_map_lookup(&mp_globals_get()->map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); - if (elem != NULL) { - *(byte*)ip = (elem - &mp_globals_get()->map.table[0]) & 0xff; - PUSH(elem->value); - } else { - PUSH(mp_load_global(MP_OBJ_QSTR_VALUE(key))); - } + obj = mp_load_global(qst); } + PUSH(obj); ip++; DISPATCH(); } @@ -323,6 +404,7 @@ dispatch_loop: #if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE ENTRY(MP_BC_LOAD_ATTR): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; SET_TOP(mp_load_attr(TOP(), qst)); @@ -330,30 +412,22 @@ dispatch_loop: } #else ENTRY(MP_BC_LOAD_ATTR): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_obj_t top = TOP(); + mp_map_elem_t *elem = NULL; if (mp_obj_is_instance_type(mp_obj_get_type(top))) { mp_obj_instance_t *self = MP_OBJ_TO_PTR(top); - mp_uint_t x = *ip; - mp_obj_t key = MP_OBJ_NEW_QSTR(qst); - mp_map_elem_t *elem; - if (x < self->members.alloc && self->members.table[x].key == key) { - elem = &self->members.table[x]; - } else { - elem = mp_map_lookup(&self->members, key, MP_MAP_LOOKUP); - if (elem != NULL) { - *(byte*)ip = elem - &self->members.table[0]; - } else { - goto load_attr_cache_fail; - } - } - SET_TOP(elem->value); - ip++; - DISPATCH(); + elem = mp_map_cached_lookup(&self->members, qst, (uint8_t*)ip); } - load_attr_cache_fail: - SET_TOP(mp_load_attr(top, qst)); + mp_obj_t obj; + if (elem != NULL) { + obj = elem->value; + } else { + obj = mp_load_attr(top, qst); + } + SET_TOP(obj); ip++; DISPATCH(); } @@ -415,6 +489,7 @@ dispatch_loop: #if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE ENTRY(MP_BC_STORE_ATTR): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_store_attr(sp[0], qst, sp[-1]); @@ -428,31 +503,20 @@ dispatch_loop: // consequence of this is that we can't use MP_MAP_LOOKUP_ADD_IF_NOT_FOUND // in the fast-path below, because that store could override a property. ENTRY(MP_BC_STORE_ATTR): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; + mp_map_elem_t *elem = NULL; mp_obj_t top = TOP(); if (mp_obj_is_instance_type(mp_obj_get_type(top)) && sp[-1] != MP_OBJ_NULL) { mp_obj_instance_t *self = MP_OBJ_TO_PTR(top); - mp_uint_t x = *ip; - mp_obj_t key = MP_OBJ_NEW_QSTR(qst); - mp_map_elem_t *elem; - if (x < self->members.alloc && self->members.table[x].key == key) { - elem = &self->members.table[x]; - } else { - elem = mp_map_lookup(&self->members, key, MP_MAP_LOOKUP); - if (elem != NULL) { - *(byte*)ip = elem - &self->members.table[0]; - } else { - goto store_attr_cache_fail; - } - } - elem->value = sp[-1]; - sp -= 2; - ip++; - DISPATCH(); + elem = mp_map_cached_lookup(&self->members, qst, (uint8_t*)ip); + } + if (elem != NULL) { + elem->value = sp[-1]; + } else { + mp_store_attr(sp[0], qst, sp[-1]); } - store_attr_cache_fail: - mp_store_attr(sp[0], qst, sp[-1]); sp -= 2; ip++; DISPATCH(); @@ -649,21 +713,28 @@ unwind_jump:; while ((unum & 0x7f) > 0) { unum -= 1; assert(exc_sp >= exc_stack); - if (MP_TAGPTR_TAG1(exc_sp->val_sp) && exc_sp->handler > ip) { - // Getting here the stack looks like: - // (..., X, dest_ip) - // where X is pointed to by exc_sp->val_sp and in the case - // of a "with" block contains the context manager info. - // We're going to run "finally" code as a coroutine - // (not calling it recursively). Set up a sentinel - // on the stack so it can return back to us when it is - // done (when WITH_CLEANUP or END_FINALLY reached). - // The sentinel is the number of exception handlers left to - // unwind, which is a non-negative integer. - PUSH(MP_OBJ_NEW_SMALL_INT(unum)); - ip = exc_sp->handler; // get exception handler byte code address - exc_sp--; // pop exception handler - goto dispatch_loop; // run the exception handler + + if (MP_TAGPTR_TAG1(exc_sp->val_sp)) { + if (exc_sp->handler > ip) { + // Found a finally handler that isn't active; run it. + // Getting here the stack looks like: + // (..., X, dest_ip) + // where X is pointed to by exc_sp->val_sp and in the case + // of a "with" block contains the context manager info. + assert(&sp[-1] == MP_TAGPTR_PTR(exc_sp->val_sp)); + // We're going to run "finally" code as a coroutine + // (not calling it recursively). Set up a sentinel + // on the stack so it can return back to us when it is + // done (when WITH_CLEANUP or END_FINALLY reached). + // The sentinel is the number of exception handlers left to + // unwind, which is a non-negative integer. + PUSH(MP_OBJ_NEW_SMALL_INT(unum)); + ip = exc_sp->handler; + goto dispatch_loop; + } else { + // Found a finally handler that is already active; cancel it. + CANCEL_ACTIVE_FINALLY(sp); + } } POP_EXC_BLOCK(); } @@ -691,9 +762,9 @@ unwind_jump:; // if TOS is None, just pops it and continues // if TOS is an integer, finishes coroutine and returns control to caller // if TOS is an exception, reraises the exception + assert(exc_sp >= exc_stack); + POP_EXC_BLOCK(); if (TOP() == mp_const_none) { - assert(exc_sp >= exc_stack); - POP_EXC_BLOCK(); sp--; } else if (mp_obj_is_small_int(TOP())) { // We finished "finally" coroutine and now dispatch back @@ -738,6 +809,7 @@ unwind_jump:; } ENTRY(MP_BC_FOR_ITER): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward code_state->sp = sp; @@ -753,6 +825,12 @@ unwind_jump:; ip += ulab; // jump to after for-block } else { PUSH(value); // push the next iteration value + #if MICROPY_PY_SYS_SETTRACE + // LINE event should trigger for every iteration so invalidate last trigger + if (code_state->frame) { + code_state->frame->lineno = 0; + } + #endif } DISPATCH(); } @@ -887,6 +965,7 @@ unwind_jump:; } ENTRY(MP_BC_CALL_FUNCTION): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_UINT; // unum & 0xff == n_positional @@ -896,7 +975,7 @@ unwind_jump:; if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { code_state->ip = ip; code_state->sp = sp; - code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, 0); + code_state->exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp); mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1); #if !MICROPY_ENABLE_PYSTACK if (new_state == NULL) { @@ -921,6 +1000,7 @@ unwind_jump:; } ENTRY(MP_BC_CALL_FUNCTION_VAR_KW): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_UINT; // unum & 0xff == n_positional @@ -932,7 +1012,7 @@ unwind_jump:; if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { code_state->ip = ip; code_state->sp = sp; - code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, 0); + code_state->exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp); mp_call_args_t out_args; mp_call_prepare_args_n_kw_var(false, unum, sp, &out_args); @@ -966,6 +1046,7 @@ unwind_jump:; } ENTRY(MP_BC_CALL_METHOD): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_UINT; // unum & 0xff == n_positional @@ -975,7 +1056,7 @@ unwind_jump:; if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { code_state->ip = ip; code_state->sp = sp; - code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, 0); + code_state->exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp); size_t n_args = unum & 0xff; size_t n_kw = (unum >> 8) & 0xff; @@ -1004,6 +1085,7 @@ unwind_jump:; } ENTRY(MP_BC_CALL_METHOD_VAR_KW): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_UINT; // unum & 0xff == n_positional @@ -1015,7 +1097,7 @@ unwind_jump:; if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { code_state->ip = ip; code_state->sp = sp; - code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, 0); + code_state->exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp); mp_call_args_t out_args; mp_call_prepare_args_n_kw_var(true, unum, sp, &out_args); @@ -1053,28 +1135,32 @@ unwind_jump:; unwind_return: // Search for and execute finally handlers that aren't already active while (exc_sp >= exc_stack) { - if (MP_TAGPTR_TAG1(exc_sp->val_sp) && exc_sp->handler > ip) { - // Found a finally handler that isn't active. - // Getting here the stack looks like: - // (..., X, [iter0, iter1, ...,] ret_val) - // where X is pointed to by exc_sp->val_sp and in the case - // of a "with" block contains the context manager info. - // There may be 0 or more for-iterators between X and the - // return value, and these must be removed before control can - // pass to the finally code. We simply copy the ret_value down - // over these iterators, if they exist. If they don't then the - // following is a null operation. - mp_obj_t *finally_sp = MP_TAGPTR_PTR(exc_sp->val_sp); - finally_sp[1] = sp[0]; - sp = &finally_sp[1]; - // We're going to run "finally" code as a coroutine - // (not calling it recursively). Set up a sentinel - // on a stack so it can return back to us when it is - // done (when WITH_CLEANUP or END_FINALLY reached). - PUSH(MP_OBJ_NEW_SMALL_INT(-1)); - ip = exc_sp->handler; - POP_EXC_BLOCK(); - goto dispatch_loop; + if (MP_TAGPTR_TAG1(exc_sp->val_sp)) { + if (exc_sp->handler > ip) { + // Found a finally handler that isn't active; run it. + // Getting here the stack looks like: + // (..., X, [iter0, iter1, ...,] ret_val) + // where X is pointed to by exc_sp->val_sp and in the case + // of a "with" block contains the context manager info. + // There may be 0 or more for-iterators between X and the + // return value, and these must be removed before control can + // pass to the finally code. We simply copy the ret_value down + // over these iterators, if they exist. If they don't then the + // following is a null operation. + mp_obj_t *finally_sp = MP_TAGPTR_PTR(exc_sp->val_sp); + finally_sp[1] = sp[0]; + sp = &finally_sp[1]; + // We're going to run "finally" code as a coroutine + // (not calling it recursively). Set up a sentinel + // on a stack so it can return back to us when it is + // done (when WITH_CLEANUP or END_FINALLY reached). + PUSH(MP_OBJ_NEW_SMALL_INT(-1)); + ip = exc_sp->handler; + goto dispatch_loop; + } else { + // Found a finally handler that is already active; cancel it. + CANCEL_ACTIVE_FINALLY(sp); + } } POP_EXC_BLOCK(); } @@ -1096,37 +1182,39 @@ unwind_return: #endif code_state = new_code_state; *code_state->sp = res; - goto run_code_state; + goto run_code_state_from_return; } #endif + FRAME_LEAVE(); return MP_VM_RETURN_NORMAL; - ENTRY(MP_BC_RAISE_VARARGS): { + ENTRY(MP_BC_RAISE_LAST): { MARK_EXC_IP_SELECTIVE(); - mp_uint_t unum = *ip; - mp_obj_t obj; - if (unum == 2) { - mp_warning(NULL, "exception chaining not supported"); - // ignore (pop) "from" argument - sp--; - } - if (unum == 0) { - // search for the inner-most previous exception, to reraise it - obj = MP_OBJ_NULL; - for (mp_exc_stack_t *e = exc_sp; e >= exc_stack; e--) { - if (e->prev_exc != NULL) { - obj = MP_OBJ_FROM_PTR(e->prev_exc); - break; - } + // search for the inner-most previous exception, to reraise it + mp_obj_t obj = MP_OBJ_NULL; + for (mp_exc_stack_t *e = exc_sp; e >= exc_stack; --e) { + if (e->prev_exc != NULL) { + obj = MP_OBJ_FROM_PTR(e->prev_exc); + break; } - if (obj == MP_OBJ_NULL) { - obj = mp_obj_new_exception_msg(&mp_type_RuntimeError, "no active exception to reraise"); - RAISE(obj); - } - } else { - obj = TOP(); } - obj = mp_make_raise_obj(obj); + if (obj == MP_OBJ_NULL) { + obj = mp_obj_new_exception_msg(&mp_type_RuntimeError, "no active exception to reraise"); + } + RAISE(obj); + } + + ENTRY(MP_BC_RAISE_OBJ): { + MARK_EXC_IP_SELECTIVE(); + mp_obj_t obj = mp_make_raise_obj(TOP()); + RAISE(obj); + } + + ENTRY(MP_BC_RAISE_FROM): { + MARK_EXC_IP_SELECTIVE(); + mp_warning(NULL, "exception chaining not supported"); + sp--; // ignore (pop) "from" argument + mp_obj_t obj = mp_make_raise_obj(TOP()); RAISE(obj); } @@ -1135,7 +1223,8 @@ yield: nlr_pop(); code_state->ip = ip; code_state->sp = sp; - code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, 0); + code_state->exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp); + FRAME_LEAVE(); return MP_VM_RETURN_YIELD; ENTRY(MP_BC_YIELD_FROM): { @@ -1177,21 +1266,15 @@ yield: DISPATCH(); } else { assert(ret_kind == MP_VM_RETURN_EXCEPTION); + assert(!EXC_MATCH(ret_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))); // Pop exhausted gen sp--; - if (EXC_MATCH(ret_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))) { - PUSH(mp_obj_exception_get_value(ret_value)); - // If we injected GeneratorExit downstream, then even - // if it was swallowed, we re-raise GeneratorExit - GENERATOR_EXIT_IF_NEEDED(t_exc); - DISPATCH(); - } else { - RAISE(ret_value); - } + RAISE(ret_value); } } ENTRY(MP_BC_IMPORT_NAME): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_obj_t obj = POP(); @@ -1200,6 +1283,7 @@ yield: } ENTRY(MP_BC_IMPORT_FROM): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_obj_t obj = mp_import_from(TOP(), qst); @@ -1214,7 +1298,7 @@ yield: #if MICROPY_OPT_COMPUTED_GOTO ENTRY(MP_BC_LOAD_CONST_SMALL_INT_MULTI): - PUSH(MP_OBJ_NEW_SMALL_INT((mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - 16)); + PUSH(MP_OBJ_NEW_SMALL_INT((mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS)); DISPATCH(); ENTRY(MP_BC_LOAD_FAST_MULTI): @@ -1242,19 +1326,19 @@ yield: MARK_EXC_IP_SELECTIVE(); #else ENTRY_DEFAULT: - if (ip[-1] < MP_BC_LOAD_CONST_SMALL_INT_MULTI + 64) { - PUSH(MP_OBJ_NEW_SMALL_INT((mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - 16)); + if (ip[-1] < MP_BC_LOAD_CONST_SMALL_INT_MULTI + MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM) { + PUSH(MP_OBJ_NEW_SMALL_INT((mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS)); DISPATCH(); - } else if (ip[-1] < MP_BC_LOAD_FAST_MULTI + 16) { + } else if (ip[-1] < MP_BC_LOAD_FAST_MULTI + MP_BC_LOAD_FAST_MULTI_NUM) { obj_shared = fastn[MP_BC_LOAD_FAST_MULTI - (mp_int_t)ip[-1]]; goto load_check; - } else if (ip[-1] < MP_BC_STORE_FAST_MULTI + 16) { + } else if (ip[-1] < MP_BC_STORE_FAST_MULTI + MP_BC_STORE_FAST_MULTI_NUM) { fastn[MP_BC_STORE_FAST_MULTI - (mp_int_t)ip[-1]] = POP(); DISPATCH(); - } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NUM_BYTECODE) { + } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + MP_BC_UNARY_OP_MULTI_NUM) { SET_TOP(mp_unary_op(ip[-1] - MP_BC_UNARY_OP_MULTI, TOP())); DISPATCH(); - } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + MP_BINARY_OP_NUM_BYTECODE) { + } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + MP_BC_BINARY_OP_MULTI_NUM) { mp_obj_t rhs = POP(); mp_obj_t lhs = TOP(); SET_TOP(mp_binary_op(ip[-1] - MP_BC_BINARY_OP_MULTI, lhs, rhs)); @@ -1262,9 +1346,11 @@ yield: } else #endif { - mp_obj_t obj = mp_obj_new_exception_msg(&mp_type_NotImplementedError, "byte code not implemented"); + + mp_obj_t obj = mp_obj_new_exception_msg(&mp_type_NotImplementedError, "opcode"); nlr_pop(); code_state->state[0] = obj; + FRAME_LEAVE(); return MP_VM_RETURN_EXCEPTION; } @@ -1355,24 +1441,32 @@ exception_handler: } } + #if MICROPY_PY_SYS_SETTRACE + // Exceptions are traced here + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t*)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_Exception))) { + TRACE_TICK(code_state->ip, code_state->sp, true /* yes, it's an exception */); + } + #endif + #if MICROPY_STACKLESS unwind_loop: #endif - // set file and line number that the exception occurred at - // TODO: don't set traceback for exceptions re-raised by END_FINALLY. - // But consider how to handle nested exceptions. - if (nlr.ret_val != &mp_const_GeneratorExit_obj) { + // Set traceback info (file and line number) where the exception occurred, but not for: + // - constant GeneratorExit object, because it's const + // - exceptions re-raised by END_FINALLY + // - exceptions re-raised explicitly by "raise" + if (nlr.ret_val != &mp_const_GeneratorExit_obj + && *code_state->ip != MP_BC_END_FINALLY + && *code_state->ip != MP_BC_RAISE_LAST) { const byte *ip = code_state->fun_bc->bytecode; - ip = mp_decode_uint_skip(ip); // skip n_state - ip = mp_decode_uint_skip(ip); // skip n_exc_stack - ip++; // skip scope_params - ip++; // skip n_pos_args - ip++; // skip n_kwonly_args - ip++; // skip n_def_pos_args - size_t bc = code_state->ip - ip; - size_t code_info_size = mp_decode_uint_value(ip); - ip = mp_decode_uint_skip(ip); // skip code_info_size - bc -= code_info_size; + MP_BC_PRELUDE_SIG_DECODE(ip); + MP_BC_PRELUDE_SIZE_DECODE(ip); + const byte *bytecode_start = ip + n_info + n_cell; + #if !MICROPY_PERSISTENT_CODE + // so bytecode is aligned + bytecode_start = MP_ALIGN(bytecode_start, sizeof(mp_uint_t)); + #endif + size_t bc = code_state->ip - bytecode_start; #if MICROPY_PERSISTENT_CODE qstr block_name = ip[0] | (ip[1] << 8); qstr source_file = ip[2] | (ip[3] << 8); @@ -1383,29 +1477,7 @@ unwind_loop: qstr source_file = mp_decode_uint_value(ip); ip = mp_decode_uint_skip(ip); #endif - size_t source_line = 1; - size_t c; - while ((c = *ip)) { - size_t b, l; - if ((c & 0x80) == 0) { - // 0b0LLBBBBB encoding - b = c & 0x1f; - l = c >> 5; - ip += 1; - } else { - // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) - b = c & 0xf; - l = ((c << 4) & 0x700) | ip[1]; - ip += 2; - } - if (bc >= b) { - bc -= b; - source_line += l; - } else { - // found source line corresponding to bytecode offset - break; - } - } + size_t source_line = mp_bytecode_get_source_line(ip, bc); mp_obj_exception_add_traceback(MP_OBJ_FROM_PTR(nlr.ret_val), source_file, source_line, block_name); } @@ -1444,11 +1516,11 @@ unwind_loop: mp_nonlocal_free(code_state, sizeof(mp_code_state_t)); #endif code_state = new_code_state; - size_t n_state = mp_decode_uint_value(code_state->fun_bc->bytecode); + size_t n_state = code_state->n_state; fastn = &code_state->state[n_state - 1]; exc_stack = (mp_exc_stack_t*)(code_state->state + n_state); // variables that are visible to the exception handler (declared volatile) - exc_sp = MP_TAGPTR_PTR(code_state->exc_sp); // stack grows up, exc_sp points to top of stack + exc_sp = MP_CODE_STATE_EXC_SP_IDX_TO_PTR(exc_stack, code_state->exc_sp_idx); // stack grows up, exc_sp points to top of stack goto unwind_loop; #endif @@ -1456,6 +1528,7 @@ unwind_loop: // propagate exception to higher level // Note: ip and sp don't have usable values at this point code_state->state[0] = MP_OBJ_FROM_PTR(nlr.ret_val); // put exception here because sp is invalid + FRAME_LEAVE(); return MP_VM_RETURN_EXCEPTION; } } diff --git a/python/src/py/vmentrytable.h b/python/src/py/vmentrytable.h index 641c8ee42..7f55a4806 100644 --- a/python/src/py/vmentrytable.h +++ b/python/src/py/vmentrytable.h @@ -99,17 +99,19 @@ static const void *const entry_table[256] = { [MP_BC_CALL_METHOD] = &&entry_MP_BC_CALL_METHOD, [MP_BC_CALL_METHOD_VAR_KW] = &&entry_MP_BC_CALL_METHOD_VAR_KW, [MP_BC_RETURN_VALUE] = &&entry_MP_BC_RETURN_VALUE, - [MP_BC_RAISE_VARARGS] = &&entry_MP_BC_RAISE_VARARGS, + [MP_BC_RAISE_LAST] = &&entry_MP_BC_RAISE_LAST, + [MP_BC_RAISE_OBJ] = &&entry_MP_BC_RAISE_OBJ, + [MP_BC_RAISE_FROM] = &&entry_MP_BC_RAISE_FROM, [MP_BC_YIELD_VALUE] = &&entry_MP_BC_YIELD_VALUE, [MP_BC_YIELD_FROM] = &&entry_MP_BC_YIELD_FROM, [MP_BC_IMPORT_NAME] = &&entry_MP_BC_IMPORT_NAME, [MP_BC_IMPORT_FROM] = &&entry_MP_BC_IMPORT_FROM, [MP_BC_IMPORT_STAR] = &&entry_MP_BC_IMPORT_STAR, - [MP_BC_LOAD_CONST_SMALL_INT_MULTI ... MP_BC_LOAD_CONST_SMALL_INT_MULTI + 63] = &&entry_MP_BC_LOAD_CONST_SMALL_INT_MULTI, - [MP_BC_LOAD_FAST_MULTI ... MP_BC_LOAD_FAST_MULTI + 15] = &&entry_MP_BC_LOAD_FAST_MULTI, - [MP_BC_STORE_FAST_MULTI ... MP_BC_STORE_FAST_MULTI + 15] = &&entry_MP_BC_STORE_FAST_MULTI, - [MP_BC_UNARY_OP_MULTI ... MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NUM_BYTECODE - 1] = &&entry_MP_BC_UNARY_OP_MULTI, - [MP_BC_BINARY_OP_MULTI ... MP_BC_BINARY_OP_MULTI + MP_BINARY_OP_NUM_BYTECODE - 1] = &&entry_MP_BC_BINARY_OP_MULTI, + [MP_BC_LOAD_CONST_SMALL_INT_MULTI ... MP_BC_LOAD_CONST_SMALL_INT_MULTI + MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM - 1] = &&entry_MP_BC_LOAD_CONST_SMALL_INT_MULTI, + [MP_BC_LOAD_FAST_MULTI ... MP_BC_LOAD_FAST_MULTI + MP_BC_LOAD_FAST_MULTI_NUM - 1] = &&entry_MP_BC_LOAD_FAST_MULTI, + [MP_BC_STORE_FAST_MULTI ... MP_BC_STORE_FAST_MULTI + MP_BC_STORE_FAST_MULTI_NUM - 1] = &&entry_MP_BC_STORE_FAST_MULTI, + [MP_BC_UNARY_OP_MULTI ... MP_BC_UNARY_OP_MULTI + MP_BC_UNARY_OP_MULTI_NUM - 1] = &&entry_MP_BC_UNARY_OP_MULTI, + [MP_BC_BINARY_OP_MULTI ... MP_BC_BINARY_OP_MULTI + MP_BC_BINARY_OP_MULTI_NUM - 1] = &&entry_MP_BC_BINARY_OP_MULTI, }; #if __clang__ From 42fa0e7fbcaec92c5785d68868e24ee640184519 Mon Sep 17 00:00:00 2001 From: wow <11897160+wow0000@users.noreply.github.com> Date: Sun, 23 Sep 2018 00:15:02 +0200 Subject: [PATCH 58/79] More friendly french translations --- apps/settings/base.fr.i18n | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/settings/base.fr.i18n b/apps/settings/base.fr.i18n index 389710fad..bada0de3d 100644 --- a/apps/settings/base.fr.i18n +++ b/apps/settings/base.fr.i18n @@ -36,6 +36,6 @@ FontSizes = "Police Python" LargeFont = "Grand " SmallFont = "Petit " SoftwareVersion = "Version du logiciel" -SerialNumber = "Numéro série" -UpdatePopUp = "Rappel mise à jour" -BetaPopUp = "Rappel version bêta" +SerialNumber = "Numéro de série" +UpdatePopUp = "Rappel de mise à jour" +BetaPopUp = "Rappel de version bêta" From e399472577d9ed28d64e797040abb1637e231355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20ANDR=C3=89?= Date: Sun, 5 Apr 2020 09:07:10 +0200 Subject: [PATCH 59/79] Anglicismes --- apps/probability/base.fr.i18n | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/probability/base.fr.i18n b/apps/probability/base.fr.i18n index 056fa00f6..95da17e67 100644 --- a/apps/probability/base.fr.i18n +++ b/apps/probability/base.fr.i18n @@ -10,13 +10,13 @@ UniformDistribution = "Loi uniforme" ExponentialDistribution = "Loi exponentielle" GeometricDistribution = "Loi géométrique" PoissonDistribution = "Loi de Poisson" -ChiSquaredDistribution = "Loi du chi2" +ChiSquaredDistribution = "Loi du Khi-2" StudentDistribution = "Loi de Student" FisherDistribution = "Loi de Fisher" ChooseParameters = "Choisir les paramètres" RepetitionNumber = "n : Nombre de répétitions" SuccessProbability = "p : Probabilité de succès" -IntervalDefinition = "[a,b] : Intervalle" +IntervalDefinition = "[a;b] : Intervalle" LambdaExponentialDefinition = "λ : Paramètre" MeanDefinition = "μ : Espérance ou moyenne" DeviationDefinition = "σ : Écart type" From 73f34f8f3de2815b9dcd278361a6578c9bfa3dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20ANDR=C3=89?= Date: Mon, 6 Apr 2020 08:21:07 +0200 Subject: [PATCH 60/79] Relecture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit – zéro → racine. Il s’agit de la terminologie du programme de [1re spécialité](https://cache.media.education.gouv.fr/file/SP1-MEN-22-1-2019/16/8/spe632_annexe_1063168.pdf) – homogénéisation avec autres applications --- apps/graph/base.fr.i18n | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/graph/base.fr.i18n b/apps/graph/base.fr.i18n index d95a5ae63..dffa6585f 100644 --- a/apps/graph/base.fr.i18n +++ b/apps/graph/base.fr.i18n @@ -4,7 +4,7 @@ FunctionTab = "Fonctions" AddFunction = "Ajouter une fonction" DeleteFunction = "Supprimer la fonction" CurveType = "Type de courbe" -CartesianType = "Cartésien " +CartesianType = "Cartésienne " PolarType = "Polaire " ParametricType = "Paramétrique " IntervalT = "Intervalle t" @@ -16,18 +16,18 @@ NoFunction = "Aucune fonction" NoActivatedFunction = "Aucune fonction activée" PlotOptions = "Options de la courbe" Compute = "Calculer" -Zeros = "Zéros" +Zeros = "Racines" Tangent = "Tangente" Intersection = "Intersection" Preimage = "Antécédent" -SelectLowerBound = "Sélectionner la borne inférieure" -SelectUpperBound = "Sélectionner la borne supérieure" +SelectLowerBound = "Sélectionner la borne inférieure " +SelectUpperBound = "Sélectionner la borne supérieure " NoMaximumFound = "Aucun maximum trouvé" NoMinimumFound = "Aucun minimum trouvé" -NoZeroFound = "Aucun zéro trouvé" +NoZeroFound = "Aucune racine trouvée" NoIntersectionFound = "Aucune intersection trouvée" NoPreimageFound = "Aucun antécédent trouvé" DerivativeFunctionColumn = "Colonne de la fonction dérivée" HideDerivativeColumn = "Masquer la fonction dérivée" -AllowedCharactersAZaz09 = "Caractères autorisés : A-Z, a-z, 0-9, _" +AllowedCharactersAZaz09 = "Caractères autorisés : A..Z, a..z, 0..9, _" ReservedName = "Nom réservé" From 1fd216c97e04130ec6d6a4454171e64ace988b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20ANDR=C3=89?= Date: Tue, 7 Apr 2020 12:09:14 +0200 Subject: [PATCH 61/79] Margins, message --- apps/graph/base.pt.i18n | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/graph/base.pt.i18n b/apps/graph/base.pt.i18n index 49ec2be81..449b4da90 100644 --- a/apps/graph/base.pt.i18n +++ b/apps/graph/base.pt.i18n @@ -20,8 +20,8 @@ Zeros = "Raízes" Tangent = "Tangente" Intersection = "Intersecção" Preimage = "Imagem inversa" -SelectLowerBound = "Selecionar limite superior" -SelectUpperBound = "Selecionar limite inferior" +SelectLowerBound = "Selecionar limite superior " +SelectUpperBound = "Selecionar limite inferior " NoMaximumFound = "Nenhum máximo encontrado" NoMinimumFound = "Nenhum mínimo encontrado" NoZeroFound = "Nenhuma raiz encontrada" @@ -29,5 +29,5 @@ NoIntersectionFound = "Nenhuma intersecção encontrada" NoPreimageFound = "Nenhuma imagem inversa encontrada" DerivativeFunctionColumn = "Coluna da função derivada" HideDerivativeColumn = "Esconder função derivada" -AllowedCharactersAZaz09 = "Caracteres permitidos : A-Z, a-z, 0-9, _" +AllowedCharactersAZaz09 = "Caracteres permitidos : A..Z, a..z, 0..9, _" ReservedName = "Nome reservado" From 53e15787cdc05aa367e9085373e5a735e9d4b952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20ANDR=C3=89?= Date: Tue, 7 Apr 2020 12:08:46 +0200 Subject: [PATCH 62/79] Margins, message --- apps/graph/base.es.i18n | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/graph/base.es.i18n b/apps/graph/base.es.i18n index c2200311d..967511092 100644 --- a/apps/graph/base.es.i18n +++ b/apps/graph/base.es.i18n @@ -20,8 +20,8 @@ Zeros = "Raíces" Tangent = "Tangente" Intersection = "Intersección" Preimage = "Imagen inversa" -SelectLowerBound = "Seleccionar el límite inferior" -SelectUpperBound = "Seleccionar el límite superior" +SelectLowerBound = "Seleccionar el límite inferior " +SelectUpperBound = "Seleccionar el límite superior " NoMaximumFound = "Níngun máximo encontrado" NoMinimumFound = "Níngun mínimo encontrado" NoZeroFound = "Ninguna raíz encontrada" @@ -29,5 +29,5 @@ NoIntersectionFound = "Ninguna intersección encontrada" NoPreimageFound = "Ninguna imagen inversa encontrada" DerivativeFunctionColumn = "Columna de la derivada" HideDerivativeColumn = "Ocultar la derivada" -AllowedCharactersAZaz09 = "Caracteres permitidos : A-Z, a-z, 0-9, _" +AllowedCharactersAZaz09 = "Caracteres permitidos : A..Z, a..z, 0..9, _" ReservedName = "Nombre reservado" From b30eb20ac239296b810425c1d40f1a289b8711fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20ANDR=C3=89?= Date: Tue, 7 Apr 2020 12:08:15 +0200 Subject: [PATCH 63/79] Margins, message --- apps/graph/base.en.i18n | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/graph/base.en.i18n b/apps/graph/base.en.i18n index c65a3370e..347425517 100644 --- a/apps/graph/base.en.i18n +++ b/apps/graph/base.en.i18n @@ -20,8 +20,8 @@ Zeros = "Zeros" Tangent = "Tangent" Intersection = "Intersection" Preimage = "Inverse image" -SelectLowerBound = "Select lower bound" -SelectUpperBound = "Select upper bound" +SelectLowerBound = "Select lower bound " +SelectUpperBound = "Select upper bound " NoMaximumFound = "No maximum found" NoMinimumFound = "No minimum found" NoZeroFound = "No zero found" @@ -29,5 +29,5 @@ NoIntersectionFound = "No intersection found" NoPreimageFound = "No inverse image found" DerivativeFunctionColumn = "Derivative function column" HideDerivativeColumn = "Hide the derivative function" -AllowedCharactersAZaz09 = "Allowed characters: A-Z, a-z, 0-9, _" +AllowedCharactersAZaz09 = "Allowed characters: A.Z, a..z, 0..9, _" ReservedName = "Reserved name" From ee56f02f2b6b45b5780eac7c2bb8c79d54123a72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20ANDR=C3=89?= Date: Tue, 7 Apr 2020 12:07:48 +0200 Subject: [PATCH 64/79] Margin, messages --- apps/graph/base.de.i18n | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/graph/base.de.i18n b/apps/graph/base.de.i18n index 3eed42db5..c816e3d06 100644 --- a/apps/graph/base.de.i18n +++ b/apps/graph/base.de.i18n @@ -20,8 +20,8 @@ Zeros = "Nullstellen" Tangent = "Tangente" Intersection = "Schnittmenge" Preimage = "Urbild" -SelectLowerBound = "Untere Integrationsgrenze" -SelectUpperBound = "Obere Integrationsgrenze" +SelectLowerBound = "Untere Integrationsgrenze " +SelectUpperBound = "Obere Integrationsgrenze " NoMaximumFound = "Kein Maximalwert gefunden" NoMinimumFound = "Kein Mindestwert gefunden" NoZeroFound = "Keine Nullstelle gefunden" @@ -29,5 +29,5 @@ NoIntersectionFound = "Kein Schnittpunkt gefunden" NoPreimageFound = "Kein Urbild gefunden" DerivativeFunctionColumn = "Spalte der Ableitungsfunktion" HideDerivativeColumn = "Ableitungsfunktion ausblenden" -AllowedCharactersAZaz09 = "Erlaubte Zeichen: A-Z, a-z, 0-9, _" +AllowedCharactersAZaz09 = "Erlaubte Zeichen: A..Z, a..z, 0..9, _" ReservedName = "Reserviertes Wort" From c36323167397e12d8dcac3a65516680e9ff261c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 7 Apr 2020 12:06:01 +0200 Subject: [PATCH 65/79] [build] --silent option in rule_for_arch_executable prevent from running 'make V=1' and getting the expected output --- build/helpers.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/helpers.mk b/build/helpers.mk index ab9934192..f44d56a26 100644 --- a/build/helpers.mk +++ b/build/helpers.mk @@ -51,7 +51,7 @@ define rule_for_arch_executable .PRECIOUS: $$(BUILD_DIR)/$(1)/%.$$(EXE) $$(BUILD_DIR)/$(1)/%.$$(EXE): force_remake $(Q) echo "MAKE ARCH=$(1)" - $(Q) $$(MAKE) ARCH=$(1) --silent $$*.$$(EXE) + $(Q) $$(MAKE) ARCH=$(1) $$*.$$(EXE) endef $(foreach ARCH,$(ARCHS),$(eval $(call rule_for_arch_executable,$(ARCH)))) From 3ae1dcd9bffb128581e9c38252bfb4143233ac1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20ANDR=C3=89?= Date: Sun, 5 Apr 2020 09:00:34 +0200 Subject: [PATCH 66/79] =?UTF-8?q?retours=20=C3=A0=20la=20ligne=20aidant=20?= =?UTF-8?q?=C3=A0=20la=20lecture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/usb/base.fr.i18n | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/usb/base.fr.i18n b/apps/usb/base.fr.i18n index 5f821d5c4..aeda714e8 100644 --- a/apps/usb/base.fr.i18n +++ b/apps/usb/base.fr.i18n @@ -2,6 +2,6 @@ USBConnected = "LA CALCULATRICE EST CONNECTÉE" ConnectedMessage1 = "Pour transférer des données, connectez-" ConnectedMessage2 = "vous depuis votre ordinateur sur le site" ConnectedMessage3 = "workshop.numworks.com" -ConnectedMessage4 = "Appuyez sur la touche RETOUR de la" -ConnectedMessage5 = "calculatrice ou débranchez-la pour la" -ConnectedMessage6 = "déconnecter." +ConnectedMessage4 = "Appuyez sur la touche RETOUR" +ConnectedMessage5 = "de la calculatrice ou débranchez-la" +ConnectedMessage6 = "pour la déconnecter." From a3e91fd3ffa6bfa5f0ba10f15f1332425d08cac7 Mon Sep 17 00:00:00 2001 From: Quentin Date: Tue, 7 Apr 2020 15:01:44 +0200 Subject: [PATCH 67/79] [Fix] Fix conflicts ._. --- .github/workflows/ci-workflow.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index f7cc179b3..a67660ad2 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -9,8 +9,6 @@ jobs: with: submodules: true - run: make -j2 PLATFORM=simulator TARGET=android - - run: make -j2 PLATFORM=simulator TARGET=android epsilon.official.apk - - run: make -j2 PLATFORM=simulator TARGET=android test.apk - uses: actions/upload-artifact@master with: name: epsilon-android.apk @@ -29,7 +27,6 @@ jobs: - run: make -j2 MODEL=n0100 epsilon.onboarding.beta.dfu - run: make -j2 MODEL=n0100 flasher.light.dfu - run: make -j2 MODEL=n0100 flasher.verbose.dfu - - run: make -j2 MODEL=n0100 test.elf - uses: actions/upload-artifact@master with: name: epsilon-n0100.dfu @@ -50,7 +47,6 @@ jobs: - run: make -j2 flasher.verbose.dfu - run: make -j2 bench.ram.dfu - run: make -j2 bench.flash.dfu - - run: make -j2 test.elf - uses: actions/upload-artifact@master with: name: epsilon-n0110.dfu @@ -77,11 +73,8 @@ jobs: with: submodules: true - run: make -j2 PLATFORM=simulator - - run: make -j2 PLATFORM=simulator epsilon.official.bin - - run: make -j2 PLATFORM=simulator test.headless.bin - - run: output/release/simulator/linux/test.headless.bin - uses: actions/upload-artifact@master with: name: epsilon-linux.bin path: output/release/simulator/linux/epsilon.bin - - run: make -j2 PLATFORM=simulator test.headless.bin + - run: make -j2 PLATFORM=simulator test.headless.bin \ No newline at end of file From 9272d4e75ffcf46bae8ad80febd8e7106d1daf76 Mon Sep 17 00:00:00 2001 From: David <0b101@users.noreply.github.com> Date: Mon, 25 Mar 2019 17:01:03 -0500 Subject: [PATCH 68/79] Increase Brightness Levels to 12 --- apps/global_preferences.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/global_preferences.h b/apps/global_preferences.h index 50f9aa0f1..fb82cbe0c 100644 --- a/apps/global_preferences.h +++ b/apps/global_preferences.h @@ -23,7 +23,7 @@ public: void setBrightnessLevel(int brightnessLevel); const KDFont * font() const { return m_font; } void setFont(const KDFont * font) { m_font = font; } - constexpr static int NumberOfBrightnessStates = 5; + constexpr static int NumberOfBrightnessStates = 12; private: static_assert(I18n::NumberOfLanguages > 0, "I18n::NumberOfLanguages is not superior to 0"); // There should already have be an error when processing an empty EPSILON_I18N flag GlobalPreferences() : From ed7e6bec9d3ad875bb229788c99e5c68f3f9e004 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Thu, 21 Nov 2019 17:02:58 +0100 Subject: [PATCH 69/79] [build/device] Generate a RAM map --- build/device/ram_map.py | 64 ++++++++++++++++++++++++++++++++++++++++ build/targets.device.mak | 6 ++++ 2 files changed, 70 insertions(+) create mode 100644 build/device/ram_map.py diff --git a/build/device/ram_map.py b/build/device/ram_map.py new file mode 100644 index 000000000..d97ab9e04 --- /dev/null +++ b/build/device/ram_map.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +import re +import subprocess +import sys +import matplotlib.pyplot as plt +from matplotlib.ticker import FormatStrFormatter + +readelf_line_regex = re.compile("[0-9]+:\s+([0-9a-f]+)\s+([0-9]+)\s+[A-Z]+") +def parse_line(line): + hex_start, dec_size = re.findall(readelf_line_regex, line)[0] + return (int(hex_start, 16), int(dec_size)) + +readelf_output = subprocess.check_output([ + "arm-none-eabi-readelf", + "-W", # Don't limit line lenght + "-s", # Sizes + sys.argv[1] +]).decode('utf-8') + +for line in readelf_output.splitlines(): + words = line.split() + if not words: + continue + symbol = words[-1] + if symbol == "_ZN3Ion17staticStorageAreaE": + storage = parse_line(line) + if symbol == "_ZZN13AppsContainer19sharedAppsContainerEvE20appsContainerStorage": + container = parse_line(line) + if symbol == "_stack_start": + stack_start, _ = parse_line(line) + if symbol == "_stack_end": + stack_end, _ = parse_line(line) + if symbol == "_heap_start": + heap_start, _ = parse_line(line) + if symbol == "_heap_end": + heap_end, _ = parse_line(line) + +stack_size = stack_start - stack_end # Stack grows downwards +stack = (stack_end, stack_size) +heap_size = heap_end - heap_start +heap = (heap_start, heap_size) + +def format_kb(i): + return ("%d KB" % (i/1024)) + +fig,ax = plt.subplots() + +def plot(value, name, c): + ax.broken_barh([value], (0, 1), color=c, label=name + " - " + format_kb(value[1])) + +plot(container, "Container", "blue") +plot(storage, "Storage", "red") +plot(heap, "Heap", "pink") +plot(stack, "Stack", "green") + +ax.set_yticks([]) +ax.set_xticks(list(range(0x20000000,0x20040001,0x10000))) +xlabels = map(lambda t: '0x%08X' % int(t), ax.get_xticks()) +ax.set_xticklabels(xlabels); +ax.legend() +fig.set_size_inches(20, 2) +fig.savefig(sys.argv[2]) + diff --git a/build/targets.device.mak b/build/targets.device.mak index 3ad973562..4965fa00f 100644 --- a/build/targets.device.mak +++ b/build/targets.device.mak @@ -3,6 +3,12 @@ include build/targets.device.$(MODEL).mak HANDY_TARGETS += flasher.light flasher.verbose bench.ram bench.flash HANDY_TARGETS_EXTENSIONS += dfu hex bin +$(eval $(call rule_for, \ + RAMSIZE, %_ram_map.png, %.elf, \ + $$(PYTHON) build/device/ram_map.py $$< $$@, \ + local \ +)) + .PHONY: %_size %_size: $(BUILD_DIR)/%.$(EXE) @echo "========= BUILD OUTPUT ========" From 35d446353fc31a897de3e6234576efd33cf9f82b Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Tue, 7 Apr 2020 14:54:28 -0400 Subject: [PATCH 70/79] [escher] Avoid long switches on Left/Up/Right/Down --- escher/src/layout_field.cpp | 87 ++++++++++++++++------- ion/include/ion/events.h | 1 + poincare/include/poincare/layout_cursor.h | 4 +- 3 files changed, 66 insertions(+), 26 deletions(-) diff --git a/escher/src/layout_field.cpp b/escher/src/layout_field.cpp index 6a7086c10..dd4268182 100644 --- a/escher/src/layout_field.cpp +++ b/escher/src/layout_field.cpp @@ -502,15 +502,46 @@ bool LayoutField::privateHandleEvent(Ion::Events::Event event) { return false; } -static inline bool IsSimpleMoveEvent(Ion::Events::Event event) { - return event == Ion::Events::Left - || event == Ion::Events::Right - || event == Ion::Events::Up - || event == Ion::Events::Down; +#define static_assert_immediately_follows(a, b) static_assert( \ + static_cast(a) + 1 == static_cast(b), \ + "Ordering error" \ +) + +#define static_assert_sequential(a, b, c, d) \ + static_assert_immediately_follows(a, b); \ + static_assert_immediately_follows(b, c); \ + static_assert_immediately_follows(c, d); + +static_assert_sequential( + LayoutCursor::Direction::Left, + LayoutCursor::Direction::Up, + LayoutCursor::Direction::Down, + LayoutCursor::Direction::Right +); + +static_assert_sequential( + Ion::Events::Left, + Ion::Events::Up, + Ion::Events::Down, + Ion::Events::Right +); + +static inline bool IsMoveEvent(Ion::Events::Event event) { + return + static_cast(event) >= static_cast(Ion::Events::Left) && + static_cast(event) <= static_cast(Ion::Events::Right); +} + +static inline LayoutCursor::Direction DirectionForMoveEvent(Ion::Events::Event event) { + assert(IsMoveEvent(event)); + return static_cast( + static_cast(LayoutCursor::Direction::Left) + + static_cast(event) - static_cast(Ion::Events::Left) + ); } bool LayoutField::privateHandleMoveEvent(Ion::Events::Event event, bool * shouldRecomputeLayout) { - if (!IsSimpleMoveEvent(event)) { + if (!IsMoveEvent(event)) { return false; } if (resetSelection()) { @@ -518,16 +549,7 @@ bool LayoutField::privateHandleMoveEvent(Ion::Events::Event event, bool * should return true; } LayoutCursor result; - if (event == Ion::Events::Left) { - result = m_contentView.cursor()->cursorAtDirection(LayoutCursor::Direction::Left, shouldRecomputeLayout); - } else if (event == Ion::Events::Right) { - result = m_contentView.cursor()->cursorAtDirection(LayoutCursor::Direction::Right, shouldRecomputeLayout); - } else if (event == Ion::Events::Up) { - result = m_contentView.cursor()->cursorAtDirection(LayoutCursor::Direction::Up, shouldRecomputeLayout); - } else { - assert(event == Ion::Events::Down); - result = m_contentView.cursor()->cursorAtDirection(LayoutCursor::Direction::Down, shouldRecomputeLayout); - } + result = m_contentView.cursor()->cursorAtDirection(DirectionForMoveEvent(event), shouldRecomputeLayout); if (result.isDefined()) { m_contentView.setCursor(result); return true; @@ -535,20 +557,37 @@ bool LayoutField::privateHandleMoveEvent(Ion::Events::Event event, bool * should return false; } -bool eventIsSelection(Ion::Events::Event event) { - return event == Ion::Events::ShiftLeft || event == Ion::Events::ShiftRight || event == Ion::Events::ShiftUp || event == Ion::Events::ShiftDown; +static_assert_sequential( + Ion::Events::ShiftLeft, + Ion::Events::ShiftUp, + Ion::Events::ShiftDown, + Ion::Events::ShiftRight +); + +static inline bool IsSelectionEvent(Ion::Events::Event event) { + return + static_cast(event) >= static_cast(Ion::Events::ShiftLeft) && + static_cast(event) <= static_cast(Ion::Events::ShiftRight); +} + +static inline LayoutCursor::Direction DirectionForSelectionEvent(Ion::Events::Event event) { + assert(IsSelectionEvent(event)); + return static_cast( + static_cast(LayoutCursor::Direction::Left) + + static_cast(event) - static_cast(Ion::Events::ShiftLeft) + ); } bool LayoutField::privateHandleSelectionEvent(Ion::Events::Event event, bool * shouldRecomputeLayout) { - if (!eventIsSelection(event)) { + if (!IsSelectionEvent(event)) { return false; } Layout addedSelection; - LayoutCursor::Direction direction = event == Ion::Events::ShiftLeft ? LayoutCursor::Direction::Left : - (event == Ion::Events::ShiftRight ? LayoutCursor::Direction::Right : - (event == Ion::Events::ShiftUp ? LayoutCursor::Direction::Up : - LayoutCursor::Direction::Down)); - LayoutCursor result = m_contentView.cursor()->selectAtDirection(direction, shouldRecomputeLayout, &addedSelection); + LayoutCursor result = m_contentView.cursor()->selectAtDirection( + DirectionForSelectionEvent(event), + shouldRecomputeLayout, + &addedSelection + ); if (addedSelection.isUninitialized()) { return false; } diff --git a/ion/include/ion/events.h b/ion/include/ion/events.h index 058bdfcbc..e046aeaee 100644 --- a/ion/include/ion/events.h +++ b/ion/include/ion/events.h @@ -18,6 +18,7 @@ public: constexpr Event(int i) : m_id(i){} // TODO: Assert here that i>=0 && i<255 uint8_t id() const { return m_id; } + constexpr explicit operator uint8_t() const { return m_id; } #if DEBUG const char * name() const; #endif diff --git a/poincare/include/poincare/layout_cursor.h b/poincare/include/poincare/layout_cursor.h index 33efc29c6..8979d7b53 100644 --- a/poincare/include/poincare/layout_cursor.h +++ b/poincare/include/poincare/layout_cursor.h @@ -29,9 +29,9 @@ public: enum class Direction { Left, - Right, Up, - Down + Down, + Right }; enum class Position { From 743eb104bf74485288fc93e8f944c57406d5ffea Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Tue, 7 Apr 2020 14:59:19 -0400 Subject: [PATCH 71/79] [ion] Replace Ion::Events::Event::id() by an explicit cast operator --- escher/src/run_loop.cpp | 2 +- ion/include/ion/events.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/escher/src/run_loop.cpp b/escher/src/run_loop.cpp index 6c9ead568..4653324ff 100644 --- a/escher/src/run_loop.cpp +++ b/escher/src/run_loop.cpp @@ -53,7 +53,7 @@ bool RunLoop::step() { } #if ESCHER_LOG_EVENTS_BINARY - Ion::Console::writeChar((char)event.id()); + Ion::Console::writeChar(static_cast(event)); #endif #if ESCHER_LOG_EVENTS_NAME const char * name = event.name(); diff --git a/ion/include/ion/events.h b/ion/include/ion/events.h index e046aeaee..2b09c0795 100644 --- a/ion/include/ion/events.h +++ b/ion/include/ion/events.h @@ -17,7 +17,6 @@ public: constexpr Event() : m_id(4*PageSize){} // Return Ion::Event::None by default constexpr Event(int i) : m_id(i){} // TODO: Assert here that i>=0 && i<255 - uint8_t id() const { return m_id; } constexpr explicit operator uint8_t() const { return m_id; } #if DEBUG const char * name() const; From b59085bd7f415386485b569fa47574e37ebe5cbd Mon Sep 17 00:00:00 2001 From: FloMiau <37121807+FloMiau@users.noreply.github.com> Date: Sat, 4 Jan 2020 10:27:24 +0100 Subject: [PATCH 72/79] Update German language file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit replace "Testmodus" with "Prüfungsmodus" Real: "Reel" -> "Reell" Gradians: "Gone" -> "Gon" --- apps/settings/base.de.i18n | 10 +++++----- apps/shared.de.i18n | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n index d726da86e..e8684369d 100644 --- a/apps/settings/base.de.i18n +++ b/apps/settings/base.de.i18n @@ -6,9 +6,9 @@ EditionMode = "Eingabe" EditionLinear = "Linear " Edition2D = "Natürlich " ComplexFormat = "Komplex" -ExamMode = "Testmodus" -ExamModeActive = "Wieder starten Testmodus" -ToDeactivateExamMode1 = "Um den Testmodus auszuschalten," +ExamMode = "Prüfungsmodus" +ExamModeActive = "Wieder starten Modus" +ToDeactivateExamMode1 = "Um den Prüfungsmodus auszuschalten," ToDeactivateExamMode2 = "schließen Sie den Rechner an einen" ToDeactivateExamMode3 = "Computer oder eine Steckdose an." # --------------------- Please do not edit these messages --------------------- @@ -22,13 +22,13 @@ AboutWarning4 = "for any resulting damage." # ----------------------------------------------------------------------------- About = "Über" Degrees = "Grad " -Gradians = "Gone " +Gradians = "Gon " Radian = "Bogenmaß " Decimal = "Dezimal " Engineering = "Technisch " Scientific = "Wissenschaftlich " SignificantFigures = "Signifikante Stellen " -Real = "Reel " +Real = "Reell " Cartesian = "Algebraische " Polar = "Polar " Brightness = "Helligkeit" diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index 0329c99cb..f6ae43bfd 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -1,9 +1,9 @@ ActivateDeactivate = "Aktivieren/Deaktivieren" ActivateDutchExamMode = "Activate Dutch exam mode" -ActivateExamMode = "Starten Testmodus" +ActivateExamMode = "Starten Prüfungsmodus" ActiveExamModeMessage1 = "Alle Ihre Daten werden " ActiveExamModeMessage2 = "gelöscht, wenn Sie den " -ActiveExamModeMessage3 = "Testmodus einschalten." +ActiveExamModeMessage3 = "Prüfungsmodus einschalten." ActiveDutchExamModeMessage1 = "All your data will be deleted when" ActiveDutchExamModeMessage2 = "you activate the exam mode. Python" ActiveDutchExamModeMessage3 = "application will be unavailable." @@ -20,8 +20,8 @@ Deviation = "Varianz" DisplayValues = "Werte anzeigen" Empty = "Leer" Eng = "tech" -ExitExamMode1 = "Möchten Sie den Testmodus " -ExitExamMode2 = "verlassen?" +ExitExamMode1 = "Möchten Sie den" +ExitExamMode2 = "Prüfungsmodus verlassen?" Exponential = "Exponentielle" FillWithFormula = "Mit einer Formel füllen" ForbiddenValue = "Verbotener Wert" From 64d0b3107f607f09ad9582840ee2f0949887e87f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Sat, 30 Nov 2019 20:12:38 +0100 Subject: [PATCH 73/79] [apps] Remove statically-allocated buffers --- apps/battery_view.cpp | 9 ++++----- apps/hardware_test/arrow_view.cpp | 5 ++--- apps/lock_view.cpp | 5 ++--- apps/shared/ok_view.cpp | 5 ++--- apps/shared/round_cursor_view.cpp | 8 ++++---- apps/solver/equation_list_view.cpp | 9 ++++----- 6 files changed, 18 insertions(+), 23 deletions(-) diff --git a/apps/battery_view.cpp b/apps/battery_view.cpp index 9e8b97e9c..abd03c731 100644 --- a/apps/battery_view.cpp +++ b/apps/battery_view.cpp @@ -54,9 +54,6 @@ bool BatteryView::setIsPlugged(bool isPlugged) { return false; } -KDColor s_flashWorkingBuffer[BatteryView::k_flashHeight*BatteryView::k_flashWidth]; -KDColor s_tickWorkingBuffer[BatteryView::k_tickHeight*BatteryView::k_tickWidth]; - void BatteryView::drawRect(KDContext * ctx, KDRect rect) const { assert(m_chargeState != Ion::Battery::Charge::EMPTY); /* We draw from left to right. The middle part representing the battery @@ -72,7 +69,8 @@ void BatteryView::drawRect(KDContext * ctx, KDRect rect) const { // Charging: Yellow background with flash ctx->fillRect(KDRect(batteryInsideX, 0, batteryInsideWidth, k_batteryHeight), Palette::YellowLight); KDRect frame((k_batteryWidth-k_flashWidth)/2, 0, k_flashWidth, k_flashHeight); - ctx->blendRectWithMask(frame, KDColorWhite, (const uint8_t *)flashMask, s_flashWorkingBuffer); + KDColor flashWorkingBuffer[BatteryView::k_flashHeight*BatteryView::k_flashWidth]; + ctx->blendRectWithMask(frame, KDColorWhite, (const uint8_t *)flashMask, flashWorkingBuffer); } else if (m_chargeState == Ion::Battery::Charge::LOW) { assert(!m_isPlugged); // Low: Quite empty battery @@ -91,7 +89,8 @@ void BatteryView::drawRect(KDContext * ctx, KDRect rect) const { if (m_isPlugged) { // Plugged and full: Full battery with tick KDRect frame((k_batteryWidth-k_tickWidth)/2, (k_batteryHeight-k_tickHeight)/2, k_tickWidth, k_tickHeight); - ctx->blendRectWithMask(frame, Palette::YellowDark, (const uint8_t *)tickMask, s_tickWorkingBuffer); + KDColor tickWorkingBuffer[BatteryView::k_tickHeight*BatteryView::k_tickWidth]; + ctx->blendRectWithMask(frame, Palette::YellowDark, (const uint8_t *)tickMask, tickWorkingBuffer); } } diff --git a/apps/hardware_test/arrow_view.cpp b/apps/hardware_test/arrow_view.cpp index ad5173c04..f74583986 100644 --- a/apps/hardware_test/arrow_view.cpp +++ b/apps/hardware_test/arrow_view.cpp @@ -48,16 +48,15 @@ void ArrowView::setColor(KDColor color) { } } -KDColor s_arrowWorkingBuffer[10*9]; - void ArrowView::drawRect(KDContext * ctx, KDRect rect) const { + KDColor arrowWorkingBuffer[10*9]; ctx->fillRect(bounds(), KDColorWhite); KDCoordinate startLine = m_directionIsUp ? k_arrowHeight : 0; KDCoordinate startArrow = m_directionIsUp ? 0 : bounds().height()-k_arrowHeight; ctx->fillRect(KDRect((Ion::Display::Width-k_arrowThickness)/2, startLine, k_arrowThickness, bounds().height()-k_arrowHeight), m_color); KDRect frame((Ion::Display::Width-k_arrowWidth)/2, startArrow, k_arrowWidth, k_arrowHeight); const uint8_t * mask = m_directionIsUp ? (const uint8_t *)arrowUpMask : (const uint8_t *)arrowDownMask; - ctx->blendRectWithMask(frame, m_color, mask, s_arrowWorkingBuffer); + ctx->blendRectWithMask(frame, m_color, mask, arrowWorkingBuffer); } } diff --git a/apps/lock_view.cpp b/apps/lock_view.cpp index ed41abc72..9a0113907 100644 --- a/apps/lock_view.cpp +++ b/apps/lock_view.cpp @@ -12,11 +12,10 @@ const uint8_t lockMask[LockView::k_lockHeight][LockView::k_lockWidth] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, }; -KDColor s_lockWorkingBuffer[LockView::k_lockHeight*LockView::k_lockWidth]; - void LockView::drawRect(KDContext * ctx, KDRect rect) const { KDRect frame((bounds().width() - k_lockWidth)/2, (bounds().height()-k_lockHeight)/2, k_lockWidth, k_lockHeight); - ctx->blendRectWithMask(frame, KDColorWhite, (const uint8_t *)lockMask, s_lockWorkingBuffer); + KDColor lockWorkingBuffer[LockView::k_lockHeight*LockView::k_lockWidth]; + ctx->blendRectWithMask(frame, KDColorWhite, (const uint8_t *)lockMask, lockWorkingBuffer); } KDSize LockView::minimalSizeForOptimalDisplay() const { diff --git a/apps/shared/ok_view.cpp b/apps/shared/ok_view.cpp index bb49b69b2..d524e5725 100644 --- a/apps/shared/ok_view.cpp +++ b/apps/shared/ok_view.cpp @@ -25,13 +25,12 @@ const uint8_t okMask[OkView::k_okSize][OkView::k_okSize] = { {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, }; -KDColor s_okWorkingBuffer[OkView::k_okSize*OkView::k_okSize]; - void OkView::drawRect(KDContext * ctx, KDRect rect) const { KDCoordinate width = bounds().width(); KDCoordinate height = bounds().height(); KDRect frame((width-k_okSize)/2, (height-k_okSize)/2, k_okSize, k_okSize); - ctx->blendRectWithMask(frame, KDColorBlack, (const uint8_t *)okMask, s_okWorkingBuffer); + KDColor okWorkingBuffer[OkView::k_okSize*OkView::k_okSize]; + ctx->blendRectWithMask(frame, KDColorBlack, (const uint8_t *)okMask, okWorkingBuffer); } KDSize OkView::minimalSizeForOptimalDisplay() const { diff --git a/apps/shared/round_cursor_view.cpp b/apps/shared/round_cursor_view.cpp index 790817b7d..10b14e86b 100644 --- a/apps/shared/round_cursor_view.cpp +++ b/apps/shared/round_cursor_view.cpp @@ -2,10 +2,9 @@ namespace Shared { -static KDColor s_cursorWorkingBuffer[Dots::LargeDotDiameter*Dots::LargeDotDiameter]; - void RoundCursorView::drawRect(KDContext * ctx, KDRect rect) const { KDRect r = bounds(); + KDColor cursorWorkingBuffer[Dots::LargeDotDiameter*Dots::LargeDotDiameter]; #ifdef GRAPH_CURSOR_SPEEDUP /* Beware that only the pixels of the intersection of rect with KDContext's * clipping rect are pulled. All other pixels are left unaltered. Indeed @@ -15,7 +14,7 @@ void RoundCursorView::drawRect(KDContext * ctx, KDRect rect) const { ctx->getPixels(r, m_underneathPixelBuffer); m_underneathPixelBufferLoaded = true; #endif - ctx->blendRectWithMask(r, m_color, (const uint8_t *)Dots::LargeDotMask, s_cursorWorkingBuffer); + ctx->blendRectWithMask(r, m_color, (const uint8_t *)Dots::LargeDotMask, cursorWorkingBuffer); } KDSize RoundCursorView::minimalSizeForOptimalDisplay() const { @@ -67,10 +66,11 @@ bool RoundCursorView::eraseCursorIfPossible() { return false; } // Erase the cursor + KDColor cursorWorkingBuffer[Dots::LargeDotDiameter*Dots::LargeDotDiameter]; KDContext * ctx = KDIonContext::sharedContext(); ctx->setOrigin(currentFrame.origin()); ctx->setClippingRect(currentFrame); - ctx->fillRectWithPixels(KDRect(0,0,k_cursorSize, k_cursorSize), m_underneathPixelBuffer, s_cursorWorkingBuffer); + ctx->fillRectWithPixels(KDRect(0,0,k_cursorSize, k_cursorSize), m_underneathPixelBuffer, cursorWorkingBuffer); // TODO Restore the context to previous values? return true; } diff --git a/apps/solver/equation_list_view.cpp b/apps/solver/equation_list_view.cpp index 6edfd4092..f4a4d18f3 100644 --- a/apps/solver/equation_list_view.cpp +++ b/apps/solver/equation_list_view.cpp @@ -105,15 +105,14 @@ const uint8_t bottomBrace[braceExtremumHeight][braceExtremumWidth] = { {0xFF, 0xFF, 0xF7, 0x25, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, }; -KDColor s_braceWorkingBuffer[60]; - void EquationListView::BraceView::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(bounds(), KDColorWhite); KDCoordinate height = bounds().height(); KDCoordinate margin = 3; - ctx->blendRectWithMask(KDRect(margin, 0, braceExtremumWidth, braceExtremumHeight), KDColorBlack, (const uint8_t *)topBrace, (KDColor *)(s_braceWorkingBuffer)); - ctx->blendRectWithMask(KDRect(0, height/2-braceCenterHeight/2, braceCenterWidth, braceCenterHeight), KDColorBlack, (const uint8_t *)middleBrace, (KDColor *)(s_braceWorkingBuffer)); - ctx->blendRectWithMask(KDRect(margin, height-braceExtremumHeight, braceExtremumWidth, braceExtremumHeight), KDColorBlack, (const uint8_t *)bottomBrace, (KDColor *)(s_braceWorkingBuffer)); + KDColor braceWorkingBuffer[60]; + ctx->blendRectWithMask(KDRect(margin, 0, braceExtremumWidth, braceExtremumHeight), KDColorBlack, (const uint8_t *)topBrace, (KDColor *)(braceWorkingBuffer)); + ctx->blendRectWithMask(KDRect(0, height/2-braceCenterHeight/2, braceCenterWidth, braceCenterHeight), KDColorBlack, (const uint8_t *)middleBrace, (KDColor *)(braceWorkingBuffer)); + ctx->blendRectWithMask(KDRect(margin, height-braceExtremumHeight, braceExtremumWidth, braceExtremumHeight), KDColorBlack, (const uint8_t *)bottomBrace, (KDColor *)(braceWorkingBuffer)); ctx->fillRect(KDRect(margin, braceExtremumHeight, 1, height/2-braceCenterHeight/2-braceExtremumHeight), KDColorBlack); ctx->fillRect(KDRect(margin, height/2+braceCenterHeight/2, 1, height/2-braceExtremumHeight/2-braceExtremumHeight), KDColorBlack); } From 2768ac2b48cb200f5e4c80a667a150dfc2d50a06 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Sat, 30 Nov 2019 20:13:02 +0100 Subject: [PATCH 74/79] [escher] Remove statically-allocated buffers --- escher/src/chevron_view.cpp | 5 ++--- escher/src/ellipsis_view.cpp | 5 ++--- escher/src/gauge_view.cpp | 5 ++--- escher/src/key_view.cpp | 5 ++--- escher/src/switch_view.cpp | 7 +++---- 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/escher/src/chevron_view.cpp b/escher/src/chevron_view.cpp index b6c7b32d2..0ac38c794 100644 --- a/escher/src/chevron_view.cpp +++ b/escher/src/chevron_view.cpp @@ -14,8 +14,6 @@ const uint8_t chevronMask[ChevronView::k_chevronHeight][ChevronView::k_chevronWi {0x0C, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, }; -KDColor s_workingBuffer[ChevronView::k_chevronWidth*ChevronView::k_chevronHeight]; - void ChevronView::drawRect(KDContext * ctx, KDRect rect) const { /* Draw the chevron aligned on the right of the view and vertically centered. * The heightCenter is the coordinate of the vertical middle of the view. That @@ -24,7 +22,8 @@ void ChevronView::drawRect(KDContext * ctx, KDRect rect) const { KDCoordinate heightCenter = bounds().height()/2; KDCoordinate chevronHalfHeight = k_chevronHeight/2; KDRect frame(width - k_chevronWidth, heightCenter -chevronHalfHeight, k_chevronWidth, k_chevronHeight); - ctx->blendRectWithMask(frame, Palette::YellowDark, (const uint8_t *)chevronMask, s_workingBuffer); + KDColor workingBuffer[ChevronView::k_chevronWidth*ChevronView::k_chevronHeight]; + ctx->blendRectWithMask(frame, Palette::YellowDark, (const uint8_t *)chevronMask, workingBuffer); } KDSize ChevronView::minimalSizeForOptimalDisplay() const { diff --git a/escher/src/ellipsis_view.cpp b/escher/src/ellipsis_view.cpp index f965ea447..4a659b6a9 100644 --- a/escher/src/ellipsis_view.cpp +++ b/escher/src/ellipsis_view.cpp @@ -7,8 +7,6 @@ const uint8_t ellipsisMask[EllipsisView::k_ellipsisHeight][EllipsisView::k_ellip {0xFF, 0xD1, 0xA1, 0xC1, 0xFF, 0xFF, 0xFF, 0xD1, 0xA1, 0xC1, 0xFF, 0xFF, 0xFF, 0xD1, 0xA1, 0xC1, 0xFF}, }; -KDColor s_ellipsisWorkingBuffer[EllipsisView::k_ellipsisWidth*EllipsisView::k_ellipsisHeight]; - void EllipsisView::drawRect(KDContext * ctx, KDRect rect) const { /* Draw the ellipsis vertically and horizontally centered in the view. * The heightCenter is the coordinate of the vertical middle of the view. That @@ -18,7 +16,8 @@ void EllipsisView::drawRect(KDContext * ctx, KDRect rect) const { KDCoordinate heightCenter = bounds().height()/2; KDCoordinate ellipsisHalfHeight = k_ellipsisHeight/2; KDRect frame(widthCenter - ellipsisHalfWidth, heightCenter - ellipsisHalfHeight, k_ellipsisWidth, k_ellipsisHeight); - ctx->blendRectWithMask(frame, KDColorBlack, (const uint8_t *)ellipsisMask, s_ellipsisWorkingBuffer); + KDColor ellipsisWorkingBuffer[EllipsisView::k_ellipsisWidth*EllipsisView::k_ellipsisHeight]; + ctx->blendRectWithMask(frame, KDColorBlack, (const uint8_t *)ellipsisMask, ellipsisWorkingBuffer); } KDSize EllipsisView::minimalSizeForOptimalDisplay() const { diff --git a/escher/src/gauge_view.cpp b/escher/src/gauge_view.cpp index f22d7ecf6..71c0dc05d 100644 --- a/escher/src/gauge_view.cpp +++ b/escher/src/gauge_view.cpp @@ -40,18 +40,17 @@ void GaugeView::setBackgroundColor(KDColor color) { } } -KDColor s_gaugeIndicatorWorkingBuffer[GaugeView::k_indicatorDiameter*GaugeView::k_indicatorDiameter]; - void GaugeView::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(bounds(), m_backgroundColor); /* Draw the gauge centered vertically on all the width available */ KDCoordinate width = bounds().width()-k_indicatorDiameter; KDCoordinate height = bounds().height(); + KDColor gaugeIndicatorWorkingBuffer[GaugeView::k_indicatorDiameter*GaugeView::k_indicatorDiameter]; ctx->fillRect(KDRect(k_indicatorDiameter/2, (height-k_thickness)/2, width*m_level, k_thickness), Palette::YellowDark); ctx->fillRect(KDRect(k_indicatorDiameter/2+width*m_level, (height-k_thickness)/2, width*(1.0f-m_level), k_thickness), Palette::GreyDark); KDRect frame(width*m_level, (height-k_indicatorDiameter)/2, k_indicatorDiameter, k_indicatorDiameter); - ctx->blendRectWithMask(frame, Palette::YellowDark, (const uint8_t *)gaugeIndicatorMask, s_gaugeIndicatorWorkingBuffer); + ctx->blendRectWithMask(frame, Palette::YellowDark, (const uint8_t *)gaugeIndicatorMask, gaugeIndicatorWorkingBuffer); } KDSize GaugeView::minimalSizeForOptimalDisplay() const { diff --git a/escher/src/key_view.cpp b/escher/src/key_view.cpp index 1c1022f5b..d61598fec 100644 --- a/escher/src/key_view.cpp +++ b/escher/src/key_view.cpp @@ -76,14 +76,13 @@ void KeyView::setType(Type type) { markRectAsDirty(bounds()); } -KDColor s_keyWorkingBuffer[KeyView::k_keySize*KeyView::k_keySize]; - void KeyView::drawRect(KDContext * ctx, KDRect rect) const { /* Draw the key centered on the view. */ KDCoordinate width = bounds().width(); KDCoordinate height = bounds().height(); KDRect frame((width - k_keySize)/2, (height - k_keySize)/2, k_keySize, k_keySize); - ctx->blendRectWithMask(frame, KDColorBlack, mask(), s_keyWorkingBuffer); + KDColor keyWorkingBuffer[KeyView::k_keySize*KeyView::k_keySize]; + ctx->blendRectWithMask(frame, KDColorBlack, mask(), keyWorkingBuffer); } KDSize KeyView::minimalSizeForOptimalDisplay() const { diff --git a/escher/src/switch_view.cpp b/escher/src/switch_view.cpp index a88dfb7a0..b06d51620 100644 --- a/escher/src/switch_view.cpp +++ b/escher/src/switch_view.cpp @@ -46,8 +46,6 @@ void SwitchView::setState(bool state) { markRectAsDirty(bounds()); } -KDColor s_switchWorkingBuffer[SwitchView::k_switchWidth*SwitchView::k_switchHeight]; - void SwitchView::drawRect(KDContext * ctx, KDRect rect) const { /* Draw the switch aligned on the right of the view and vertically centered. * The heightCenter is the coordinate of the vertical middle of the view. That @@ -55,13 +53,14 @@ void SwitchView::drawRect(KDContext * ctx, KDRect rect) const { KDCoordinate width = bounds().width(); KDCoordinate heightCenter = bounds().height()/2; KDCoordinate switchHalfHeight = k_switchHeight/2; + KDColor switchWorkingBuffer[SwitchView::k_switchWidth*SwitchView::k_switchHeight]; KDColor mainColor = m_state ? Palette::YellowDark : Palette::GreyDark; KDRect frame(width - k_switchWidth, heightCenter -switchHalfHeight, k_switchWidth, k_switchHeight); - ctx->blendRectWithMask(frame, mainColor, (const uint8_t *)switchMask, s_switchWorkingBuffer); + ctx->blendRectWithMask(frame, mainColor, (const uint8_t *)switchMask, switchWorkingBuffer); KDCoordinate onOffX = width - (m_state ? k_onOffSize : k_switchWidth); KDRect onOffFrame(onOffX, heightCenter -switchHalfHeight, k_onOffSize, k_onOffSize); - ctx->blendRectWithMask(onOffFrame, KDColorWhite, (const uint8_t *)onOffMask, s_switchWorkingBuffer); + ctx->blendRectWithMask(onOffFrame, KDColorWhite, (const uint8_t *)onOffMask, switchWorkingBuffer); } KDSize SwitchView::minimalSizeForOptimalDisplay() const { From 2f61b823d02e0b561d6c34122fc4286b2d10bad4 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Sat, 30 Nov 2019 20:14:02 +0100 Subject: [PATCH 75/79] [poincare] Remove statically-allocated buffers --- poincare/include/poincare/parenthesis_layout.h | 1 - poincare/src/left_parenthesis_layout.cpp | 5 +++-- poincare/src/parenthesis_layout.cpp | 2 -- poincare/src/right_parenthesis_layout.cpp | 5 +++-- poincare/src/trigonometry_cheat_table.cpp | 4 ++-- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/poincare/include/poincare/parenthesis_layout.h b/poincare/include/poincare/parenthesis_layout.h index 5cc757d03..f35679205 100644 --- a/poincare/include/poincare/parenthesis_layout.h +++ b/poincare/include/poincare/parenthesis_layout.h @@ -27,7 +27,6 @@ public: #endif protected: - static KDColor s_parenthesisWorkingBuffer[k_parenthesisCurveHeight*k_parenthesisCurveWidth]; KDSize computeSize() override { return KDSize(ParenthesisWidth(), HeightGivenChildHeight(childHeight())); } diff --git a/poincare/src/left_parenthesis_layout.cpp b/poincare/src/left_parenthesis_layout.cpp index 5297a2eff..c7454d81d 100644 --- a/poincare/src/left_parenthesis_layout.cpp +++ b/poincare/src/left_parenthesis_layout.cpp @@ -25,20 +25,21 @@ const uint8_t bottomLeftCurve[ParenthesisLayoutNode::k_parenthesisCurveHeight][P }; void LeftParenthesisLayoutNode::RenderWithChildHeight(KDCoordinate childHeight, KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { + KDColor parenthesisWorkingBuffer[k_parenthesisCurveHeight*k_parenthesisCurveWidth]; KDCoordinate parenthesisHeight = ParenthesisLayoutNode::HeightGivenChildHeight(childHeight); KDRect frame(p.x()+ParenthesisLayoutNode::k_externWidthMargin, p.y()+ParenthesisLayoutNode::k_externHeightMargin, ParenthesisLayoutNode::k_parenthesisCurveWidth, ParenthesisLayoutNode::k_parenthesisCurveHeight); - ctx->blendRectWithMask(frame, expressionColor, (const uint8_t *)topLeftCurve, (KDColor *)(ParenthesisLayoutNode::s_parenthesisWorkingBuffer)); + ctx->blendRectWithMask(frame, expressionColor, (const uint8_t *)topLeftCurve, parenthesisWorkingBuffer); frame = KDRect(p.x()+ParenthesisLayoutNode::k_externWidthMargin, p.y() + parenthesisHeight - ParenthesisLayoutNode::k_parenthesisCurveHeight - ParenthesisLayoutNode::k_externHeightMargin, ParenthesisLayoutNode::k_parenthesisCurveWidth, ParenthesisLayoutNode::k_parenthesisCurveHeight); - ctx->blendRectWithMask(frame, expressionColor, (const uint8_t *)bottomLeftCurve, (KDColor *)(ParenthesisLayoutNode::s_parenthesisWorkingBuffer)); + ctx->blendRectWithMask(frame, expressionColor, (const uint8_t *)bottomLeftCurve, parenthesisWorkingBuffer); ctx->fillRect(KDRect(p.x()+ParenthesisLayoutNode::k_externWidthMargin, p.y()+ParenthesisLayoutNode::k_parenthesisCurveHeight+ParenthesisLayoutNode::k_externHeightMargin, diff --git a/poincare/src/parenthesis_layout.cpp b/poincare/src/parenthesis_layout.cpp index 40c5fc263..dece3d9d9 100644 --- a/poincare/src/parenthesis_layout.cpp +++ b/poincare/src/parenthesis_layout.cpp @@ -2,6 +2,4 @@ namespace Poincare { -KDColor ParenthesisLayoutNode::s_parenthesisWorkingBuffer[]; - } diff --git a/poincare/src/right_parenthesis_layout.cpp b/poincare/src/right_parenthesis_layout.cpp index 9f4a12eaa..09ff93132 100644 --- a/poincare/src/right_parenthesis_layout.cpp +++ b/poincare/src/right_parenthesis_layout.cpp @@ -25,20 +25,21 @@ const uint8_t bottomRightCurve[ParenthesisLayoutNode::k_parenthesisCurveHeight][ }; void RightParenthesisLayoutNode::RenderWithChildHeight(KDCoordinate childHeight, KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { + KDColor parenthesisWorkingBuffer[k_parenthesisCurveHeight*k_parenthesisCurveWidth]; KDCoordinate parenthesisHeight = ParenthesisLayoutNode::HeightGivenChildHeight(childHeight); KDRect frame = KDRect(p.x() + ParenthesisLayoutNode::k_widthMargin + ParenthesisLayoutNode::k_lineThickness - ParenthesisLayoutNode::k_parenthesisCurveWidth, p.y() + ParenthesisLayoutNode::k_externHeightMargin, ParenthesisLayoutNode::k_parenthesisCurveWidth, ParenthesisLayoutNode::k_parenthesisCurveHeight); - ctx->blendRectWithMask(frame, expressionColor, (const uint8_t *)topRightCurve, (KDColor *)(ParenthesisLayoutNode::s_parenthesisWorkingBuffer)); + ctx->blendRectWithMask(frame, expressionColor, (const uint8_t *)topRightCurve, parenthesisWorkingBuffer); frame = KDRect(p.x() + ParenthesisLayoutNode::k_widthMargin + ParenthesisLayoutNode::k_lineThickness - ParenthesisLayoutNode::k_parenthesisCurveWidth, p.y() + parenthesisHeight - ParenthesisLayoutNode::k_parenthesisCurveHeight - ParenthesisLayoutNode::k_externHeightMargin, ParenthesisLayoutNode::k_parenthesisCurveWidth, ParenthesisLayoutNode::k_parenthesisCurveHeight); - ctx->blendRectWithMask(frame, expressionColor, (const uint8_t *)bottomRightCurve, (KDColor *)(ParenthesisLayoutNode::s_parenthesisWorkingBuffer)); + ctx->blendRectWithMask(frame, expressionColor, (const uint8_t *)bottomRightCurve, parenthesisWorkingBuffer); ctx->fillRect(KDRect(p.x()+ParenthesisLayoutNode::k_widthMargin, p.y()+ParenthesisLayoutNode::k_parenthesisCurveHeight+2, diff --git a/poincare/src/trigonometry_cheat_table.cpp b/poincare/src/trigonometry_cheat_table.cpp index 9cd3f9ef0..6cf2ca3e9 100644 --- a/poincare/src/trigonometry_cheat_table.cpp +++ b/poincare/src/trigonometry_cheat_table.cpp @@ -82,7 +82,7 @@ Expression TrigonometryCheatTable::simplify(const Expression e, ExpressionNode:: * For instance, when simplfy a Cosine, we always compute the value for an angle * in the top right trigonometric quadrant. */ const TrigonometryCheatTable * TrigonometryCheatTable::Table() { - static Row sTableRows[] = { + static const Row sTableRows[] = { Row(Row::Pair("-90", -90.0f), Row::Pair("π*(-2)^(-1)", -1.5707963267948966f), Row::Pair("-100", -100.0f), @@ -306,7 +306,7 @@ const TrigonometryCheatTable * TrigonometryCheatTable::Table() { Row::Pair("0",0.0f), Row::Pair("0",0.0f)) }; - static TrigonometryCheatTable sTable(sTableRows); + static const TrigonometryCheatTable sTable(sTableRows); return &sTable; } From 883d1312a3fb8088bdf078ffd053413beb0af305 Mon Sep 17 00:00:00 2001 From: Quentin Date: Wed, 8 Apr 2020 12:37:02 +0200 Subject: [PATCH 76/79] [Fix] Fix compilation crash --- escher/Makefile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/escher/Makefile b/escher/Makefile index 9a5957cdc..343a49eaf 100644 --- a/escher/Makefile +++ b/escher/Makefile @@ -1,5 +1,13 @@ SFLAGS += -Iescher/include +$(eval $(call rule_for, \ + THEME, \ + escher/palette.h, \ + $(addprefix themes/themes/, $(addsuffix .json, $(OMEGA_THEME))), \ + $$(PYTHON) themes/themes_manager.py $(OMEGA_THEME) $$@, \ + global \ +)) + escher_src += $(addprefix escher/src/,\ alternate_empty_view_controller.cpp \ app.cpp \ @@ -98,14 +106,6 @@ $(eval $(call rule_for, \ global \ )) -$(eval $(call rule_for, \ - THEME, \ - escher/palette.h, \ - $(addprefix themes/themes/, $(addsuffix .json, $(OMEGA_THEME))), \ - $$(PYTHON) themes/themes_manager.py $(OMEGA_THEME) $$@, \ - global \ -)) - INLINER := $(BUILD_DIR)/escher/image/inliner .PRECIOUS: $(BUILD_DIR)/%.h $(BUILD_DIR)/%.cpp From 68f6bbc3783b169147ed1f4329d298d30e2c8b64 Mon Sep 17 00:00:00 2001 From: Joachim Le Fournis <43498612+RedGl0w@users.noreply.github.com> Date: Wed, 8 Apr 2020 13:23:59 +0200 Subject: [PATCH 77/79] Update Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 19b468655..8284adfa4 100644 --- a/Makefile +++ b/Makefile @@ -112,7 +112,7 @@ include quiz/Makefile # Quiz needs to be included at the end all_src = $(apps_src) $(escher_src) $(ion_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(poincare_src) $(python_src) $(runner_src) $(ion_device_flasher_src) $(ion_device_bench_src) $(tests_src) # Make palette.h a dep for every source-file. # This ensures that the theming engine works correctly. -$(call object_for,$(apps_src) $(tests_src)): $(BUILD_DIR)/escher/palette.h +$(call object_for,$(all_src)): $(BUILD_DIR)/escher/palette.h all_objs = $(call object_for,$(all_src)) .SECONDARY: $(all_objs) From 53d9a9ec25f254c2644bb896422317377bf3e56f Mon Sep 17 00:00:00 2001 From: Quentin Date: Wed, 8 Apr 2020 13:33:42 +0200 Subject: [PATCH 78/79] [GitHub] Remove metrics workflow --- .github/workflows/metrics-workflow.yml | 36 -------------------------- 1 file changed, 36 deletions(-) delete mode 100644 .github/workflows/metrics-workflow.yml diff --git a/.github/workflows/metrics-workflow.yml b/.github/workflows/metrics-workflow.yml deleted file mode 100644 index bc22fcd4b..000000000 --- a/.github/workflows/metrics-workflow.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Metrics -on: [pull_request] - -jobs: - binary-size: - runs-on: ubuntu-latest - steps: - - name: Install dependencies - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config - - name: Install ARM toolchain - uses: numworks/setup-arm-toolchain@v1 - - name: Checkout PR base - uses: actions/checkout@v2 - with: - ref: ${{ github.event.pull_request.base.sha }} - path: base - - name: Build base - run: make -j2 -C base epsilon.elf - - name: Checkout PR head - uses: actions/checkout@v2 - with: - ref: ${{ github.event.pull_request.head.sha }} - path: head - - name: Build head - run: make -j2 -C head epsilon.elf - - name: Retrieve binary size analysis - id: binary_size - run: echo "::set-output name=table::$(python3 head/build/metrics/binary_size.py base/output/release/device/n0110/epsilon.elf head/output/release/device/n0110/epsilon.elf --labels Base Head --escape)" - - name: Prepare comment auth - run: echo "::set-env name=GITHUB_TOKEN::$(echo ZGExNWM1YzNlMjVkMWU5ZGFmOWQyY2UxMmRhYjJiN2ZhMWM4ODVhMA== | base64 --decode)" - - name: Add comment - uses: actions/github@v1.0.0 - env: - GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }} - with: - args: comment ${{ steps.binary_size.outputs.table }} From ded69970abe6afc190a55dc4d250dfce71658351 Mon Sep 17 00:00:00 2001 From: Joachim Le Fournis <43498612+RedGl0w@users.noreply.github.com> Date: Wed, 8 Apr 2020 13:35:26 +0200 Subject: [PATCH 79/79] Update extapp_api.cpp --- apps/external/extapp_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/external/extapp_api.cpp b/apps/external/extapp_api.cpp index 248fc6ee2..eefd19f58 100644 --- a/apps/external/extapp_api.cpp +++ b/apps/external/extapp_api.cpp @@ -269,7 +269,7 @@ int extapp_getKey(bool allowSuspend, bool *alphaWasActive) { continue; } if (event.isKeyboardEvent()) { - key = event.id(); + key = static_cast(event); if (key == 17 || key == 4 || key == 5 || key == 52) { extapp_resetKeyboard(); }