diff options
-rw-r--r-- | src/library_fs.js | 16 | ||||
-rw-r--r-- | system/include/emscripten/bind.h | 53 | ||||
-rw-r--r-- | system/include/emscripten/val.h | 18 | ||||
-rw-r--r-- | system/include/emscripten/wire.h | 127 | ||||
-rwxr-xr-x | tests/runner.py | 23 | ||||
-rw-r--r-- | tests/test_core.py | 10 | ||||
-rw-r--r-- | tests/test_other.py | 8 | ||||
-rw-r--r-- | tools/jsrun.py | 2 |
8 files changed, 159 insertions, 98 deletions
diff --git a/src/library_fs.js b/src/library_fs.js index a6bca77c..7932385e 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -1211,8 +1211,20 @@ mergeInto(LibraryManager.library, { FS.mkdev('/dev/tty', FS.makedev(5, 0)); FS.mkdev('/dev/tty1', FS.makedev(6, 0)); // setup /dev/[u]random - FS.createDevice('/dev', 'random', function() { return Math.floor(Math.random()*256); }); - FS.createDevice('/dev', 'urandom', function() { return Math.floor(Math.random()*256); }); + var random_device; + if (typeof crypto !== 'undefined') { + // for modern web browsers + var randomBuffer = new Uint8Array(1); + random_device = function() { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; }; + } else if (ENVIRONMENT_IS_NODE) { + // for nodejs + random_device = function() { return require('crypto').randomBytes(1)[0]; }; + } else { + // default for ES5 platforms + random_device = function() { return Math.floor(Math.random()*256); }; + } + FS.createDevice('/dev', 'random', random_device); + FS.createDevice('/dev', 'urandom', random_device); // we're not going to emulate the actual shm device, // just create the tmp dirs that reside in it commonly FS.mkdir('/dev/shm'); diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 67e7c6a3..bd96f35f 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -71,7 +71,7 @@ namespace emscripten { void _embind_register_function( const char* name, unsigned argCount, - TYPEID argTypes[], + const TYPEID argTypes[], const char* signature, GenericFunction invoker, GenericFunction function); @@ -137,7 +137,7 @@ namespace emscripten { void _embind_register_class_constructor( TYPEID classType, unsigned argCount, - TYPEID argTypes[], + const TYPEID argTypes[], const char* invokerSignature, GenericFunction invoker, GenericFunction constructor); @@ -146,7 +146,7 @@ namespace emscripten { TYPEID classType, const char* methodName, unsigned argCount, - TYPEID argTypes[], + const TYPEID argTypes[], const char* invokerSignature, GenericFunction invoker, void* context, @@ -168,7 +168,7 @@ namespace emscripten { TYPEID classType, const char* methodName, unsigned argCount, - TYPEID argTypes[], + const TYPEID argTypes[], const char* invokerSignature, GenericFunction invoker, GenericFunction method); @@ -413,8 +413,8 @@ namespace emscripten { auto invoker = &Invoker<ReturnType, Args...>::invoke; _embind_register_function( name, - args.count, - args.types, + args.getCount(), + args.getTypes(), getSignature(invoker), reinterpret_cast<GenericFunction>(invoker), reinterpret_cast<GenericFunction>(fn)); @@ -760,6 +760,7 @@ namespace emscripten { } ~value_object() { + using namespace internal; _embind_finalize_value_object(internal::TypeID<ClassType>::get()); } @@ -1084,7 +1085,7 @@ namespace emscripten { class_() = delete; - explicit class_(const char* name) { + EMSCRIPTEN_ALWAYS_INLINE explicit class_(const char* name) { using namespace internal; BaseSpecifier::template verify<ClassType>(); @@ -1111,7 +1112,7 @@ namespace emscripten { } template<typename PointerType> - const class_& smart_ptr(const char* name) const { + EMSCRIPTEN_ALWAYS_INLINE const class_& smart_ptr(const char* name) const { using namespace internal; typedef smart_ptr_trait<PointerType> PointerTrait; @@ -1141,14 +1142,14 @@ namespace emscripten { }; template<typename... ConstructorArgs, typename... Policies> - const class_& constructor(Policies... policies) const { + EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(Policies... policies) const { return constructor( &internal::operator_new<ClassType, ConstructorArgs...>, policies...); } template<typename... Args, typename ReturnType, typename... Policies> - const class_& constructor(ReturnType (*factory)(Args...), Policies...) const { + EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(ReturnType (*factory)(Args...), Policies...) const { using namespace internal; // TODO: allows all raw pointers... policies need a rethink @@ -1156,8 +1157,8 @@ namespace emscripten { auto invoke = &Invoker<ReturnType, Args...>::invoke; _embind_register_class_constructor( TypeID<ClassType>::get(), - args.count, - args.types, + args.getCount(), + args.getTypes(), getSignature(invoke), reinterpret_cast<GenericFunction>(invoke), reinterpret_cast<GenericFunction>(factory)); @@ -1165,7 +1166,7 @@ namespace emscripten { } template<typename SmartPtr, typename... Args, typename... Policies> - const class_& smart_ptr_constructor(const char* smartPtrName, SmartPtr (*factory)(Args...), Policies...) const { + EMSCRIPTEN_ALWAYS_INLINE const class_& smart_ptr_constructor(const char* smartPtrName, SmartPtr (*factory)(Args...), Policies...) const { using namespace internal; smart_ptr<SmartPtr>(smartPtrName); @@ -1174,8 +1175,8 @@ namespace emscripten { auto invoke = &Invoker<SmartPtr, Args...>::invoke; _embind_register_class_constructor( TypeID<ClassType>::get(), - args.count, - args.types, + args.getCount(), + args.getTypes(), getSignature(invoke), reinterpret_cast<GenericFunction>(invoke), reinterpret_cast<GenericFunction>(factory)); @@ -1183,7 +1184,7 @@ namespace emscripten { } template<typename WrapperType, typename PointerType = WrapperType*, typename... ConstructorArgs> - const class_& allow_subclass( + EMSCRIPTEN_ALWAYS_INLINE const class_& allow_subclass( const char* wrapperClassName, const char* pointerName = "<UnknownPointerName>", ::emscripten::constructor<ConstructorArgs...> = ::emscripten::constructor<ConstructorArgs...>() @@ -1209,7 +1210,7 @@ namespace emscripten { } template<typename WrapperType, typename... ConstructorArgs> - const class_& allow_subclass( + EMSCRIPTEN_ALWAYS_INLINE const class_& allow_subclass( const char* wrapperClassName, ::emscripten::constructor<ConstructorArgs...> constructor ) const { @@ -1226,8 +1227,8 @@ namespace emscripten { _embind_register_class_function( TypeID<ClassType>::get(), methodName, - args.count, - args.types, + args.getCount(), + args.getTypes(), getSignature(invoker), reinterpret_cast<GenericFunction>(invoker), getContext(memberFunction), @@ -1245,8 +1246,8 @@ namespace emscripten { _embind_register_class_function( TypeID<ClassType>::get(), methodName, - args.count, - args.types, + args.getCount(), + args.getTypes(), getSignature(invoker), reinterpret_cast<GenericFunction>(invoker), getContext(memberFunction), @@ -1263,8 +1264,8 @@ namespace emscripten { _embind_register_class_function( TypeID<ClassType>::get(), methodName, - args.count, - args.types, + args.getCount(), + args.getTypes(), getSignature(invoke), reinterpret_cast<GenericFunction>(invoke), getContext(function), @@ -1362,8 +1363,8 @@ namespace emscripten { _embind_register_class_class_function( TypeID<ClassType>::get(), methodName, - args.count, - args.types, + args.getCount(), + args.getTypes(), getSignature(invoke), reinterpret_cast<internal::GenericFunction>(invoke), reinterpret_cast<GenericFunction>(classMethod)); @@ -1466,6 +1467,7 @@ namespace emscripten { typedef EnumType enum_type; enum_(const char* name) { + using namespace internal; _embind_register_enum( internal::TypeID<EnumType>::get(), name, @@ -1474,6 +1476,7 @@ namespace emscripten { } enum_& value(const char* name, EnumType value) { + using namespace internal; // TODO: there's still an issue here. // if EnumType is an unsigned long, then JS may receive it as a signed long static_assert(sizeof(value) <= sizeof(internal::GenericEnumValue), "enum type must fit in a GenericEnumValue"); diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index 31f5923e..78c4b60c 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -33,7 +33,7 @@ namespace emscripten { EM_VAL _emval_new( EM_VAL value, unsigned argCount, - internal::TYPEID argTypes[], + const TYPEID argTypes[], EM_VAR_ARGS argv); EM_VAL _emval_get_global(const char* name); @@ -45,14 +45,14 @@ namespace emscripten { EM_VAL _emval_call( EM_VAL value, unsigned argCount, - internal::TYPEID argTypes[], + const TYPEID argTypes[], EM_VAR_ARGS argv); // DO NOT call this more than once per signature. It will // leak generated function objects! EM_METHOD_CALLER _emval_get_method_caller( unsigned argCount, // including return value - internal::TYPEID argTypes[]); + const TYPEID argTypes[]); EM_GENERIC_WIRE_TYPE _emval_call_method( EM_METHOD_CALLER caller, EM_VAL handle, @@ -62,7 +62,7 @@ namespace emscripten { bool _emval_has_function( EM_VAL value, const char* methodName, - internal::TYPEID filter); + const TYPEID filter); EM_VAL _emval_typeof(EM_VAL value); } @@ -91,7 +91,7 @@ namespace emscripten { private: static EM_METHOD_CALLER init_method_caller() { WithPolicies<>::ArgTypeList<ReturnType, Args...> args; - return _emval_get_method_caller(args.count, args.types); + return _emval_get_method_caller(args.getCount(), args.getTypes()); } }; @@ -357,8 +357,8 @@ namespace emscripten { return val( _emval_new( handle, - argList.count, - argList.types, + argList.getCount(), + argList.getTypes(), argv)); } @@ -381,8 +381,8 @@ namespace emscripten { return val( _emval_call( handle, - argList.count, - argList.types, + argList.getCount(), + argList.getTypes(), argv)); } diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 1a9432d6..d61b0bc7 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -27,10 +27,7 @@ namespace emscripten { #endif namespace internal { - typedef void (*GenericFunction)(); - - typedef const struct _TYPEID {}* TYPEID; - + typedef const void* TYPEID; // We don't need the full std::type_info implementation. We // just need a unique identifier per type and polymorphic type @@ -38,49 +35,48 @@ namespace emscripten { template<typename T> struct CanonicalizedID { - static TYPEID get() { - static _TYPEID c; + static char c; + static constexpr TYPEID get() { return &c; } }; template<typename T> + char CanonicalizedID<T>::c; + + template<typename T> struct Canonicalized { typedef typename std::remove_cv<typename std::remove_reference<T>::type>::type type; }; template<typename T> struct LightTypeID { - static TYPEID get() { + static constexpr TYPEID get() { typedef typename Canonicalized<T>::type C; - if (has_unbound_type_names || std::is_polymorphic<C>::value) { - return reinterpret_cast<TYPEID>(&typeid(C)); - } else { - return CanonicalizedID<C>::get(); - } + return (has_unbound_type_names || std::is_polymorphic<C>::value) + ? &typeid(C) + : CanonicalizedID<C>::get(); } }; template<typename T> - const TYPEID getLightTypeID(const T& value) { + constexpr TYPEID getLightTypeID(const T& value) { typedef typename Canonicalized<T>::type C; - if (has_unbound_type_names || std::is_polymorphic<C>::value) { - return reinterpret_cast<TYPEID>(&typeid(value)); - } else { - return LightTypeID<T>::get(); - } + return (has_unbound_type_names || std::is_polymorphic<C>::value) + ? &typeid(value) + : LightTypeID<T>::get(); } template<typename T> struct TypeID { - static TYPEID get() { + static constexpr TYPEID get() { return LightTypeID<T>::get(); } }; template<typename T> struct TypeID<std::unique_ptr<T>> { - static TYPEID get() { + static constexpr TYPEID get() { return TypeID<T>::get(); } }; @@ -96,7 +92,7 @@ namespace emscripten { template<typename T> struct TypeID<AllowedRawPointer<T>> { - static TYPEID get() { + static constexpr TYPEID get() { return LightTypeID<T*>::get(); } }; @@ -125,40 +121,89 @@ namespace emscripten { }; }; - // ArgTypes<> + // TypeList<> - template<int Index, typename... Args> - struct ArgTypes; + template<typename...> + struct TypeList {}; - template<int Index> - struct ArgTypes<Index> { - template<typename... Policies> - static void fill(TYPEID* argTypes) { - } + // Cons :: T, TypeList<types...> -> Cons<T, types...> + + template<typename First, typename TypeList> + struct Cons; + + template<typename First, typename... Rest> + struct Cons<First, TypeList<Rest...>> { + typedef TypeList<First, Rest...> type; }; - template<int Index, typename T, typename... Remaining> - struct ArgTypes<Index, T, Remaining...> { - template<typename... Policies> - static void fill(TYPEID* argTypes) { - typedef typename ExecutePolicies<Policies...>::template With<T, Index>::type TransformT; - *argTypes = TypeID<TransformT>::get(); - return ArgTypes<Index + 1, Remaining...>::template fill<Policies...>(argTypes + 1); + // Apply :: T, TypeList<types...> -> T<types...> + + template<template<typename...> class Output, typename TypeList> + struct Apply; + + template<template<typename...> class Output, typename... Types> + struct Apply<Output, TypeList<Types...>> { + typedef Output<Types...> type; + }; + + // MapWithIndex_ + + template<template<size_t, typename> class Mapper, size_t CurrentIndex, typename... Args> + struct MapWithIndex_; + + template<template<size_t, typename> class Mapper, size_t CurrentIndex, typename First, typename... Rest> + struct MapWithIndex_<Mapper, CurrentIndex, First, Rest...> { + typedef typename Cons< + typename Mapper<CurrentIndex, First>::type, + typename MapWithIndex_<Mapper, CurrentIndex + 1, Rest...>::type + >::type type; + }; + + template<template<size_t, typename> class Mapper, size_t CurrentIndex> + struct MapWithIndex_<Mapper, CurrentIndex> { + typedef TypeList<> type; + }; + + template<template<typename...> class Output, template<size_t, typename> class Mapper, typename... Args> + struct MapWithIndex { + typedef typename internal::Apply< + Output, + typename MapWithIndex_<Mapper, 0, Args...>::type + >::type type; + }; + + + template<typename ArgList> + struct ArgArrayGetter; + + template<typename... Args> + struct ArgArrayGetter<TypeList<Args...>> { + static const TYPEID* get() { + static constexpr TYPEID types[] = { TypeID<Args>::get()... }; + return types; } }; // WithPolicies<...>::ArgTypeList<...> + template<typename... Policies> struct WithPolicies { + template<size_t Index, typename T> + struct MapWithPolicies { + typedef typename ExecutePolicies<Policies...>::template With<T, Index>::type type; + }; + template<typename... Args> struct ArgTypeList { - ArgTypeList() { - count = sizeof...(Args); - ArgTypes<0, Args...>::template fill<Policies...>(types); + unsigned getCount() const { + return sizeof...(Args); } - unsigned count; - TYPEID types[sizeof...(Args)]; + const TYPEID* getTypes() const { + return ArgArrayGetter< + typename MapWithIndex<TypeList, MapWithPolicies, Args...>::type + >::get(); + } }; }; diff --git a/tests/runner.py b/tests/runner.py index 6d3f55f8..e9479313 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -258,7 +258,7 @@ process(sys.argv[1]) err = '\n'.join(filter(lambda line: 'uccessfully compiled asm.js code' not in line, err.split('\n'))) return err - def run_generated_code(self, engine, filename, args=[], check_timeout=True, output_nicerizer=None): + def run_generated_code(self, engine, filename, args=[], check_timeout=True, output_nicerizer=None, assert_returncode=0): stdout = os.path.join(self.get_dir(), 'stdout') # use files, as PIPE can get too full and hang us stderr = os.path.join(self.get_dir(), 'stderr') try: @@ -266,7 +266,7 @@ process(sys.argv[1]) except: cwd = None os.chdir(self.get_dir()) - run_js(filename, engine, args, check_timeout, stdout=open(stdout, 'w'), stderr=open(stderr, 'w')) + run_js(filename, engine, args, check_timeout, stdout=open(stdout, 'w'), stderr=open(stderr, 'w'), assert_returncode=assert_returncode) if cwd is not None: os.chdir(cwd) out = open(stdout, 'r').read() @@ -445,7 +445,7 @@ process(sys.argv[1]) includes, force_c, build_ll_hook, extra_emscripten_args) ## Does a complete test - builds, runs, checks output, etc. - def do_run(self, src, expected_output, args=[], output_nicerizer=None, output_processor=None, no_build=False, main_file=None, additional_files=[], js_engines=None, post_build=None, basename='src.cpp', libraries=[], includes=[], force_c=False, build_ll_hook=None, extra_emscripten_args=[]): + def do_run(self, src, expected_output, args=[], output_nicerizer=None, output_processor=None, no_build=False, main_file=None, additional_files=[], js_engines=None, post_build=None, basename='src.cpp', libraries=[], includes=[], force_c=False, build_ll_hook=None, extra_emscripten_args=[], assert_returncode=None): if force_c or (main_file is not None and main_file[-2:]) == '.c': basename = 'src.c' Building.COMPILER = to_cc(Building.COMPILER) @@ -464,7 +464,7 @@ process(sys.argv[1]) js_engines = filter(lambda engine: engine not in self.banned_js_engines, js_engines) if len(js_engines) == 0: return self.skip('No JS engine present to run this test with. Check %s and the paths therein.' % EM_CONFIG) for engine in js_engines: - js_output = self.run_generated_code(engine, filename + '.o.js', args, output_nicerizer=output_nicerizer) + js_output = self.run_generated_code(engine, filename + '.o.js', args, output_nicerizer=output_nicerizer, assert_returncode=assert_returncode) self.assertContained(expected_output, js_output.replace('\r\n', '\n')) self.assertNotContained('ERROR', js_output) @@ -477,7 +477,7 @@ process(sys.argv[1]) test_index += 1 # No building - just process an existing .ll file (or .bc, which we turn into .ll) - def do_ll_run(self, ll_file, expected_output=None, args=[], js_engines=None, output_nicerizer=None, post_build=None, force_recompile=False, build_ll_hook=None, extra_emscripten_args=[]): + def do_ll_run(self, ll_file, expected_output=None, args=[], js_engines=None, output_nicerizer=None, post_build=None, force_recompile=False, build_ll_hook=None, extra_emscripten_args=[], assert_returncode=None): filename = os.path.join(self.get_dir(), 'src.cpp') self.prep_ll_run(filename, ll_file, force_recompile, build_ll_hook) @@ -485,12 +485,13 @@ process(sys.argv[1]) self.ll_to_js(filename, extra_emscripten_args, post_build) self.do_run(None, - expected_output, - args, - no_build=True, - js_engines=js_engines, - output_nicerizer=output_nicerizer, - post_build=None) # post_build was already done in ll_to_js, this do_run call is just to test the output + expected_output, + args, + no_build=True, + js_engines=js_engines, + output_nicerizer=output_nicerizer, + post_build=None, + assert_returncode=assert_returncode) # post_build was already done in ll_to_js, this do_run call is just to test the output # Run a server and a web page. When a test runs, we tell the server about it, diff --git a/tests/test_core.py b/tests/test_core.py index a4579800..20278d37 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6244,7 +6244,7 @@ def process(filename): ''' try: - self.do_run(src, '*nothingatall*') + self.do_run(src, '*nothingatall*', assert_returncode=None) except Exception, e: # This test *should* fail, by throwing this exception assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) @@ -6261,7 +6261,7 @@ def process(filename): Settings.SAFE_HEAP_LINES = ["src.cpp:99"] try: - self.do_run(src, '*nothingatall*') + self.do_run(src, '*nothingatall*', assert_returncode=None) except Exception, e: # This test *should* fail, by throwing this exception assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) @@ -6311,7 +6311,7 @@ def process(filename): Building.link([module_name + '.o', main_name + '.o'], all_name) try: - self.do_ll_run(all_name, '*nothingatall*') + self.do_ll_run(all_name, '*nothingatall*', assert_returncode=None) except Exception, e: # This test *should* fail, by throwing this exception assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) @@ -6328,7 +6328,7 @@ def process(filename): for lines in [["module.cpp:22", "main.cpp:9"], ["module.cpp:7", "main.cpp:29"], ["module.cpp:127", "main.cpp:449"], ["module.cpp:7"], ["main.cpp:9"]]: Settings.SAFE_HEAP_LINES = lines try: - self.do_ll_run(all_name, '*nothingatall*') + self.do_ll_run(all_name, '*nothingatall*', assert_returncode=None) except Exception, e: # This test *should* fail, by throwing this exception assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) @@ -6353,7 +6353,7 @@ def process(filename): } ''' try: - self.do_run(src, '*nothingatall*') + self.do_run(src, '*nothingatall*', assert_returncode=None) except Exception, e: # This test *should* fail assert 'Assertion failed: x < 15' in str(e), str(e) diff --git a/tests/test_other.py b/tests/test_other.py index 4fb90b1a..9e3c59e8 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -245,7 +245,7 @@ Options that are modified or new in %s include: output = Popen([PYTHON, compiler, 'twopart_main.o', '-O1', '-g'] + args, stdout=PIPE, stderr=PIPE).communicate() assert os.path.exists(target), '\n'.join(output) #print '\n'.join(output) - self.assertContained('missing function', run_js(target, stderr=STDOUT)) + self.assertContained('missing function', run_js(target, stderr=STDOUT, assert_returncode=None)) try_delete(target) # Combining those bc files into js should work @@ -549,7 +549,7 @@ f.close() print args, expected, err_expected out, err = Popen([PYTHON, EMCC, 'src.c'] + args, stderr=PIPE).communicate() if err_expected: self.assertContained(err_expected, err) - self.assertContained(expected, run_js(self.in_dir('a.out.js'), stderr=PIPE, full_output=True)) + self.assertContained(expected, run_js(self.in_dir('a.out.js'), stderr=PIPE, full_output=True, assert_returncode=None)) return open(self.in_dir('a.out.js')).read() if os.environ.get('EMCC_FAST_COMPILER') == '0': @@ -2406,7 +2406,7 @@ var Module = { print: function(x) { throw '<{(' + x + ')}>' } }; ''') Popen([PYTHON, EMCC, 'code.cpp', '--pre-js', 'pre.js']).communicate() - output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True, engine=NODE_JS) + output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True, engine=NODE_JS, assert_returncode=None) assert r'<{(123456789)}>' in output, output def test_precompiled_headers(self): @@ -2793,7 +2793,7 @@ int main() { cmd = [PYTHON, EMCC, 'src.cpp', '-O' + str(opts), '-s', 'SAFE_HEAP=' + str(safe)] print cmd Popen(cmd).communicate() - output = run_js('a.out.js', stderr=PIPE, full_output=True) + output = run_js('a.out.js', stderr=PIPE, full_output=True, assert_returncode=None) if safe: assert 'Function table mask error' in output, output else: diff --git a/tools/jsrun.py b/tools/jsrun.py index d63451db..e2ec5439 100644 --- a/tools/jsrun.py +++ b/tools/jsrun.py @@ -17,7 +17,7 @@ def timeout_run(proc, timeout=None, note='unnamed process', full_output=False): logging.info('Process ' + str(proc.pid) + ' finished after ' + str(time.time() - start) + ' seconds. Exit code: ' + str(proc.returncode)) return '\n'.join(out) if full_output else out[0] -def run_js(filename, engine=None, args=[], check_timeout=False, stdin=None, stdout=PIPE, stderr=None, cwd=None, full_output=False, assert_returncode=None): +def run_js(filename, engine=None, args=[], check_timeout=False, stdin=None, stdout=PIPE, stderr=None, cwd=None, full_output=False, assert_returncode=0): if type(engine) is not list: engine = [engine] command = engine + [filename] + (['--'] if 'd8' in engine[0] or 'jsc' in engine[0] else []) + args |