From a937bd1cf931560145a58e034e6f3c718259078d Mon Sep 17 00:00:00 2001 From: Gavin Schulz Date: Sun, 22 Mar 2015 14:16:53 -0700 Subject: [PATCH 1/6] Adds a Meta library with an eval function for interpreting code inline. --- builtin/meta.wren | 1 + script/test.py | 2 +- src/vm/wren_common.h | 7 +++++++ src/vm/wren_meta.c | 21 ++++++++++++++++++++ src/vm/wren_meta.h | 18 +++++++++++++++++ src/vm/wren_vm.c | 7 +++++++ test/meta/eval_existing_scoped_variable.wren | 5 +++++ 7 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 builtin/meta.wren create mode 100644 src/vm/wren_meta.c create mode 100644 src/vm/wren_meta.h create mode 100644 test/meta/eval_existing_scoped_variable.wren diff --git a/builtin/meta.wren b/builtin/meta.wren new file mode 100644 index 00000000..3db22bbf --- /dev/null +++ b/builtin/meta.wren @@ -0,0 +1 @@ +class Meta {} diff --git a/script/test.py b/script/test.py index 33b63888..83ed7764 100755 --- a/script/test.py +++ b/script/test.py @@ -243,7 +243,7 @@ def run_test(path): print('') -for dir in ['core', 'io', 'language', 'limit']: +for dir in ['core', 'io', 'language', 'limit', 'meta']: walk(join(WREN_DIR, 'test', dir), run_test) print_line() diff --git a/src/vm/wren_common.h b/src/vm/wren_common.h index 5d310748..380a5893 100644 --- a/src/vm/wren_common.h +++ b/src/vm/wren_common.h @@ -51,6 +51,13 @@ #define WREN_USE_LIB_IO 1 #endif +// If true, loads the "Meta" class in the standard library. +// +// Defaults to on. +#ifndef WREN_USE_LIB_META +#define WREN_USE_LIB_META 1 +#endif + // These flags are useful for debugging and hacking on Wren itself. They are not // intended to be used for production code. They default to off. diff --git a/src/vm/wren_meta.c b/src/vm/wren_meta.c new file mode 100644 index 00000000..4d65553d --- /dev/null +++ b/src/vm/wren_meta.c @@ -0,0 +1,21 @@ +#include "wren_meta.h" + +#if WREN_USE_LIB_META + +// This string literal is generated automatically from meta.wren. Do not edit. +static const char* libSource = +"class Meta {}\n"; + +void metaEval(WrenVM* vm) +{ + const char* source = wrenGetArgumentString(vm, 1); + wrenInterpret(vm, "Meta", source); +} + +void wrenLoadMetaLibrary(WrenVM* vm) +{ + wrenInterpret(vm, "", libSource); + wrenDefineStaticMethod(vm, "Meta", "eval(_)", metaEval); +} + +#endif diff --git a/src/vm/wren_meta.h b/src/vm/wren_meta.h new file mode 100644 index 00000000..7ddc37ec --- /dev/null +++ b/src/vm/wren_meta.h @@ -0,0 +1,18 @@ +#ifndef wren_meta_h +#define wren_meta_h + +#include + +#include "wren.h" +#include "wren_common.h" +#include "wren_value.h" +#include "wren_vm.h" + +// This module defines the Meta class and its associated methods. +#if WREN_USE_LIB_META + +void wrenLoadMetaLibrary(WrenVM* vm); + +#endif + +#endif diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c index d7438a6b..7bb7ab2b 100644 --- a/src/vm/wren_vm.c +++ b/src/vm/wren_vm.c @@ -14,6 +14,10 @@ #include "wren_io.h" #endif +#if WREN_USE_LIB_META + #include "wren_meta.h" +#endif + #if WREN_DEBUG_TRACE_MEMORY || WREN_DEBUG_TRACE_GC #include #endif @@ -75,6 +79,9 @@ WrenVM* wrenNewVM(WrenConfiguration* configuration) #if WREN_USE_LIB_IO wrenLoadIOLibrary(vm); #endif + #if WREN_USE_LIB_META + wrenLoadMetaLibrary(vm); + #endif return vm; } diff --git a/test/meta/eval_existing_scoped_variable.wren b/test/meta/eval_existing_scoped_variable.wren new file mode 100644 index 00000000..90c44873 --- /dev/null +++ b/test/meta/eval_existing_scoped_variable.wren @@ -0,0 +1,5 @@ +var y + +Meta.eval("y = 2") + +IO.print(y) // expect: 2 \ No newline at end of file From 31d298956564b4597e115d3bc05701e380de219e Mon Sep 17 00:00:00 2001 From: Gavin Schulz Date: Tue, 24 Mar 2015 23:30:53 -0700 Subject: [PATCH 2/6] Remove unnecessary headers. --- src/vm/wren_meta.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vm/wren_meta.h b/src/vm/wren_meta.h index 7ddc37ec..10d56700 100644 --- a/src/vm/wren_meta.h +++ b/src/vm/wren_meta.h @@ -1,12 +1,8 @@ #ifndef wren_meta_h #define wren_meta_h -#include - #include "wren.h" #include "wren_common.h" -#include "wren_value.h" -#include "wren_vm.h" // This module defines the Meta class and its associated methods. #if WREN_USE_LIB_META From 8c4e5eeea8587026db9045dc3429580c820b38ae Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Wed, 25 Mar 2015 07:26:45 -0700 Subject: [PATCH 3/6] Show standard deviation and other tweaks in benchmark script. --- script/benchmark.py | 53 ++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/script/benchmark.py b/script/benchmark.py index bd7bd4ea..b96ac66c 100755 --- a/script/benchmark.py +++ b/script/benchmark.py @@ -39,6 +39,7 @@ import sys # To generate a baseline file, run this script with "--generate-baseline". WREN_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +WREN_BIN = os.path.join(WREN_DIR, 'bin') BENCHMARK_DIR = os.path.join(WREN_DIR, 'test', 'benchmark') # How many times to run a given benchmark. @@ -78,7 +79,7 @@ BENCHMARK("map_string", r"""3645600""") BENCHMARK("string_equals", r"""3000000""") LANGUAGES = [ - ("wren", [os.path.join(WREN_DIR, 'wren')], ".wren"), + ("wren", [os.path.join(WREN_BIN, 'wren')], ".wren"), ("lua", ["lua"], ".lua"), ("luajit (-joff)", ["luajit", "-joff"], ".lua"), ("python", ["python"], ".py"), @@ -88,20 +89,22 @@ LANGUAGES = [ results = {} +if sys.platform == 'win32': + GREEN = NORMAL = RED = YELLOW = '' +else: + GREEN = '\033[32m' + NORMAL = '\033[0m' + RED = '\033[31m' + YELLOW = '\033[33m' + def green(text): - if sys.platform == 'win32': - return text - return '\033[32m' + text + '\033[0m' + return GREEN + text + NORMAL def red(text): - if sys.platform == 'win32': - return text - return '\033[31m' + text + '\033[0m' + return RED + text + NORMAL def yellow(text): - if sys.platform == 'win32': - return text - return '\033[33m' + text + '\033[0m' + return YELLOW + text + NORMAL def get_score(time): @@ -113,6 +116,20 @@ def get_score(time): return 1000.0 / time +def standard_deviation(times): + """ + Calculates the standard deviation of a list of numbers. + """ + mean = sum(times) / len(times) + + # Sum the squares of the differences from the mean. + result = 0 + for time in times: + result += (time - mean) ** 2 + + return math.sqrt(result / len(times)) + + def run_trial(benchmark, language): """Runs one benchmark one time for one language.""" args = [] @@ -180,7 +197,10 @@ def run_benchmark_language(benchmark, language, benchmark_result): if ratio < 95: comparison = red(comparison) - print(" {:5.0f} {:4.2f}s {:s}".format(score, best, comparison)) + print(" {:4.2f}s {:4.4f} {:s}".format( + best, + standard_deviation(times), + comparison)) benchmark_result[language[0]] = { "desc": name, @@ -191,7 +211,7 @@ def run_benchmark_language(benchmark, language, benchmark_result): return score -def run_benchmark(benchmark, languages): +def run_benchmark(benchmark, languages, graph): """Runs one benchmark for the given languages (or all of them).""" benchmark_result = {} @@ -203,7 +223,7 @@ def run_benchmark(benchmark, languages): num_languages += 1 run_benchmark_language(benchmark, language, benchmark_result) - if num_languages > 1: + if num_languages > 1 and graph: graph_results(benchmark_result) @@ -244,7 +264,7 @@ def read_baseline(): benchmark[2] = float(best) -def generate_baseline(): +def generate_baseline(graph): print("generating baseline") baseline_text = "" for benchmark in BENCHMARKS: @@ -300,6 +320,9 @@ def main(): parser.add_argument("--generate-baseline", action="store_true", help="Generate a baseline file") + parser.add_argument("--graph", + action="store_true", + help="Display graph results.") parser.add_argument("-l", "--language", action="append", help="Which language(s) to run benchmarks for") @@ -318,7 +341,7 @@ def main(): # Run the benchmarks. for benchmark in BENCHMARKS: if benchmark[0] == args.benchmark or args.benchmark == "all": - run_benchmark(benchmark, args.language) + run_benchmark(benchmark, args.language, args.graph) if args.output_html: print_html() From e4a785a071942f754156a5778f5949a1d7eaedca Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Wed, 25 Mar 2015 07:45:29 -0700 Subject: [PATCH 4/6] Remove oldSize from allocate API. binary_trees - wren .......... 0.29s 0.0048 107.79% relative to baseline delta_blue - wren .......... 0.12s 0.0017 106.95% relative to baseline fib - wren .......... 0.30s 0.0042 119.95% relative to baseline for - wren .......... 0.12s 0.0020 107.57% relative to baseline method_call - wren .......... 0.15s 0.0256 106.09% relative to baseline map_numeric - wren .......... 0.44s 0.0199 105.27% relative to baseline map_string - wren .......... 0.14s 0.0049 100.18% relative to baseline string_equals - wren .......... 0.30s 0.0032 100.57% relative to baseline Thanks, Michel! --- script/benchmark.py | 2 +- src/include/wren.h | 21 ++++++++++----------- src/vm/wren_vm.c | 13 +++---------- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/script/benchmark.py b/script/benchmark.py index b96ac66c..d3867240 100755 --- a/script/benchmark.py +++ b/script/benchmark.py @@ -264,7 +264,7 @@ def read_baseline(): benchmark[2] = float(best) -def generate_baseline(graph): +def generate_baseline(): print("generating baseline") baseline_text = "" for benchmark in BENCHMARKS: diff --git a/src/include/wren.h b/src/include/wren.h index eb0dd651..b32b883e 100644 --- a/src/include/wren.h +++ b/src/include/wren.h @@ -14,20 +14,19 @@ typedef struct WrenMethod WrenMethod; // A generic allocation function that handles all explicit memory management // used by Wren. It's used like so: // -// - To allocate new memory, [memory] is NULL and [oldSize] is zero. It should -// return the allocated memory or NULL on failure. +// - To allocate new memory, [memory] is NULL and [newSize] is the desired +// size. It should return the allocated memory or NULL on failure. // -// - To attempt to grow an existing allocation, [memory] is the memory, -// [oldSize] is its previous size, and [newSize] is the desired size. -// It should return [memory] if it was able to grow it in place, or a new -// pointer if it had to move it. +// - To attempt to grow an existing allocation, [memory] is the memory, and +// [newSize] is the desired size. It should return [memory] if it was able to +// grow it in place, or a new pointer if it had to move it. // -// - To shrink memory, [memory], [oldSize], and [newSize] are the same as above -// but it will always return [memory]. +// - To shrink memory, [memory] and [newSize] are the same as above but it will +// always return [memory]. // -// - To free memory, [memory] will be the memory to free and [newSize] and -// [oldSize] will be zero. It should return NULL. -typedef void* (*WrenReallocateFn)(void* memory, size_t oldSize, size_t newSize); +// - To free memory, [memory] will be the memory to free and [newSize] will be +// zero. It should return NULL. +typedef void* (*WrenReallocateFn)(void* memory, size_t newSize); // A function callable from Wren code, but implemented in C. typedef void (*WrenForeignMethodFn)(WrenVM* vm); diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c index 1e4c9279..7f7068f5 100644 --- a/src/vm/wren_vm.c +++ b/src/vm/wren_vm.c @@ -18,22 +18,15 @@ #include #endif -// The built-in reallocation function used when one is not provided by the -// configuration. -static void* defaultReallocate(void* memory, size_t oldSize, size_t newSize) -{ - return realloc(memory, newSize); -} - WrenVM* wrenNewVM(WrenConfiguration* configuration) { - WrenReallocateFn reallocate = defaultReallocate; + WrenReallocateFn reallocate = realloc; if (configuration->reallocateFn != NULL) { reallocate = configuration->reallocateFn; } - WrenVM* vm = (WrenVM*)reallocate(NULL, 0, sizeof(*vm)); + WrenVM* vm = (WrenVM*)reallocate(NULL, sizeof(*vm)); memset(vm, 0, sizeof(WrenVM)); vm->reallocate = reallocate; @@ -209,7 +202,7 @@ void* wrenReallocate(WrenVM* vm, void* memory, size_t oldSize, size_t newSize) if (newSize > 0 && vm->bytesAllocated > vm->nextGC) collectGarbage(vm); #endif - return vm->reallocate(memory, oldSize, newSize); + return vm->reallocate(memory, newSize); } // Captures the local variable [local] into an [Upvalue]. If that local is From 6b9c162fe9d8c898d1aaffef17d2e97cafb970bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 26 Mar 2015 00:01:34 +0100 Subject: [PATCH 5/6] Added myself to the AUTHORS file --- AUTHORS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 4fb330bc..95cdd04d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,4 +9,5 @@ Evan Shaw Gavin Schulz Lukas Werling Marco Lizza -Raymond Sohn +Thorbjørn Lindeijer From c707135b9a063fc83dcaa7b0f3678841e65ced35 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Wed, 25 Mar 2015 19:25:18 -0700 Subject: [PATCH 6/6] Add wren_meta to XCode project. --- project/xcode/wren.xcodeproj/project.pbxproj | 6 ++++++ src/vm/wren_meta.c | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/project/xcode/wren.xcodeproj/project.pbxproj b/project/xcode/wren.xcodeproj/project.pbxproj index c56d735b..6c693bba 100644 --- a/project/xcode/wren.xcodeproj/project.pbxproj +++ b/project/xcode/wren.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 29205C9F1AB4E6430073018D /* wren_vm.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C981AB4E6430073018D /* wren_vm.c */; }; 29C8A92F1AB71C1C00DEC81D /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 29C8A92E1AB71C1C00DEC81D /* io.c */; }; 29C8A9331AB71FFF00DEC81D /* vm.c in Sources */ = {isa = PBXBuildFile; fileRef = 29C8A9311AB71FFF00DEC81D /* vm.c */; }; + 29DE39531AC3A50A00987D41 /* wren_meta.c in Sources */ = {isa = PBXBuildFile; fileRef = 29DE39511AC3A50A00987D41 /* wren_meta.c */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -54,6 +55,8 @@ 29C8A9301AB71C3300DEC81D /* io.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = io.h; path = ../../src/cli/io.h; sourceTree = ""; }; 29C8A9311AB71FFF00DEC81D /* vm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vm.c; path = ../../src/cli/vm.c; sourceTree = ""; }; 29C8A9321AB71FFF00DEC81D /* vm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vm.h; path = ../../src/cli/vm.h; sourceTree = ""; }; + 29DE39511AC3A50A00987D41 /* wren_meta.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wren_meta.c; path = ../../src/vm/wren_meta.c; sourceTree = ""; }; + 29DE39521AC3A50A00987D41 /* wren_meta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wren_meta.h; path = ../../src/vm/wren_meta.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -79,6 +82,8 @@ 29205C941AB4E6430073018D /* wren_debug.c */, 29205CA51AB4E65E0073018D /* wren_io.h */, 29205C951AB4E6430073018D /* wren_io.c */, + 29DE39521AC3A50A00987D41 /* wren_meta.h */, + 29DE39511AC3A50A00987D41 /* wren_meta.c */, 29205CA61AB4E65E0073018D /* wren_utils.h */, 29205C961AB4E6430073018D /* wren_utils.c */, 29205CA71AB4E65E0073018D /* wren_value.h */, @@ -187,6 +192,7 @@ 29205C9E1AB4E6430073018D /* wren_value.c in Sources */, 29205C9F1AB4E6430073018D /* wren_vm.c in Sources */, 29C8A92F1AB71C1C00DEC81D /* io.c in Sources */, + 29DE39531AC3A50A00987D41 /* wren_meta.c in Sources */, 29205C8F1AB4E5C90073018D /* main.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/src/vm/wren_meta.c b/src/vm/wren_meta.c index 4d65553d..c49dea8d 100644 --- a/src/vm/wren_meta.c +++ b/src/vm/wren_meta.c @@ -8,8 +8,9 @@ static const char* libSource = void metaEval(WrenVM* vm) { - const char* source = wrenGetArgumentString(vm, 1); - wrenInterpret(vm, "Meta", source); + const char* source = wrenGetArgumentString(vm, 1); + // TODO: Type check argument. + wrenInterpret(vm, "Meta", source); } void wrenLoadMetaLibrary(WrenVM* vm)