Permalink
Switch branches/tags
1.38.10 923_var_collisions 3785-no-auto-decode PTHREAD_WORKER_PRE_JS asm_dlopen asm_dlopen2 asm2wasm-efp asm2wasm asmify async-emterp atomic_cxx_pthreads atomic_wasm atomicrmw_i64 back-to-llvm basic_vars binaryen-emulated-fp-by-default binaryen-precision break c_backend_unions c_backend cacheallrecursivetokens clean_authors cleanup-libc cleanup_test_args cmake_wasm_backend comp_opts conditionalize cpp_optimizer custom-core cxa_guard deframeworkify depreamble docs eliminate2 em-audio-sdl em-audio emcc-direct emscripten_py_only emterpreter-f32 emterpreter enable_fetch_tests endings env error_on_bad_setting eval-ctor expand-big-switches export f32 fast-fs fast-safe-heap fast-system-libs fastcomp-by-default fastpaths fetch_stream_chrome finalize_mem_init fix_class_methods fix_get_safe_internalize fix_musl_wasm_build fix_read_proxied_function_signatures fix_sanity fix_test_sse1_full fixx followups fs-mem-grow future gl_relocation glGet-cleanup globals-chunking hack heapalign html_mouse i32-mem-init improvements incoming interal-optimizer interpret_memorygrowth invliad_archive_members ising issue_3741 issue_3743 js-trap-if-js-opt land-3.5 land-1624 land-waywardmonkeys-new-libc-headers laters lazy_token_item libcxxabi_noexcept library_runtime line_endings linker-inputs linker_flags linking llvm-3.3 llvm-3.4 llvm-js llvmsvn low_globals lz4 master memcpy memimit memory-sharing merge-3.4 merge-3.5 merge-mar-13-2015 merge_37 mergefunc metadce_test mingw-path minifyJS minimal_libcxx minor_test_cleanup multithreaded-server musl-syscall native-optimizer-by-default newtriple next-merge2 no-bynterp no-eval no-exit-runtime no-fs no-idb-in-chrome no-runtime-TOTAL_MEMORY noexportinmain oc-raf old_shared_libs one-stop-shop opt_wasm_backend optimize-modules optimize_mem_init optimize_normally out_err preloading preproc proxy proxyGL proxyTransfer pthreads_metadce recent-simd reduce_fetch_error_to_warning relooper-switches-land relooper-switches relooper_opt_levels remove-asyncify remove-dynamic-alloc remove_check_call remove_fast_compiler remove_old_test remove_popen remove_run_js_helper remove_tempret0 rename_libcxx rename revert-7268-remove_get_tempret0 sdl_2d_gl side-module-alias-constant simplify-traverse simplify_suffix_handling simplifyIfs sjlj skipsanity sm small sourcemap-hack split_memory splitext standalone static_link strip_debug target_triple test_exceptions test_exit_code test_fs_workerfs_package_big test_other_cleanup to-llvm-trunk tokencache triple universal-python update_firefox_version use_node_modules vvuk-regal wasm-backend wasm-input wasm-merge wasm-metadata-input wasm-only-memset wasm-only-mine wasm-only-rel-emufp wasm-polyfill-prototype wasm-toobig wb-Ox webgl-in-worker webidl wip-wchar workerfs_package
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
8047 lines (6774 sloc) 274 KB
# Copyright 2013 The Emscripten Authors. All rights reserved.
# Emscripten is available under two separate licenses, the MIT license and the
# University of Illinois/NCSA Open Source License. Both these licenses can be
# found in the LICENSE file.
from __future__ import print_function
import glob
import hashlib
import json
import os
import random
import re
import shutil
import sys
import time
import unittest
from textwrap import dedent
from tools.shared import Building, STDOUT, PIPE, run_js, run_process, Settings, try_delete
from tools.shared import NODE_JS, V8_ENGINE, JS_ENGINES, SPIDERMONKEY_ENGINE, PYTHON, EMCC, EMAR, CLANG, WINDOWS, AUTODEBUGGER
from tools import jsrun, shared
from runner import RunnerCore, path_from_root, core_test_modes, get_bullet_library
from runner import skip_if, no_wasm_backend, needs_dlfcn, no_windows, env_modify, with_env_modify
# decorators for limiting which modes a test can run in
def SIMD(f):
def decorated(self):
if self.is_emterpreter():
self.skipTest('simd not supported in emterpreter yet')
if self.is_wasm():
self.skipTest('wasm will not support SIMD in the MVP')
self.use_all_engines = True # checks both native in spidermonkey and polyfill in others
f(self)
return decorated
def no_emterpreter(f):
return skip_if(f, 'is_emterpreter')
def no_wasm(note=''):
def decorated(f):
return skip_if(f, 'is_wasm', note)
return decorated
# Async wasm compilation can't work in some tests, they are set up synchronously
def sync(f):
def decorated(self):
if self.is_wasm():
self.emcc_args += ['-s', 'BINARYEN_ASYNC_COMPILATION=0'] # test is set up synchronously
f(self)
return decorated
def also_with_noderawfs(func):
def decorated(self):
orig_compiler_opts = Building.COMPILER_TEST_OPTS[:]
orig_args = self.emcc_args[:]
func(self)
Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-DNODERAWFS']
self.emcc_args = orig_args + ['-s', 'NODERAWFS=1']
func(self, js_engines=[NODE_JS])
return decorated
# A simple check whether the compiler arguments cause optimization.
def is_optimizing(args):
return '-O' in str(args) and '-O0' not in args
class TestCoreBase(RunnerCore):
# whether the test mode supports duplicate function elimination in js
def supports_js_dfe(self):
# wasm does this when optimizing anyhow
if self.is_wasm():
return False
supported_opt_levels = ['-O2', '-O3', '-Oz', '-Os']
for opt_level in supported_opt_levels:
if opt_level in self.emcc_args:
return True
return False
# Use closure in some tests for some additional coverage
def maybe_closure(self):
if '-O2' in self.emcc_args or '-Os' in self.emcc_args:
self.emcc_args += ['--closure', '1']
return True
return False
def do_run_in_out_file_test(self, *path, **kwargs):
test_path = path_from_root(*path)
def find_files(*ext_list):
ret = None
count = 0
for ext in ext_list:
if os.path.isfile(test_path + ext):
ret = test_path + ext
count += 1
assert count > 0, ("No file found at {} with extension {}"
.format(test_path, ext_list))
assert count <= 1, ("Test file {} found with multiple valid extensions {}"
.format(test_path, ext_list))
return ret
src = find_files('.c', '.cpp', '.cc')
output = find_files('.out', '.txt')
self.do_run_from_file(src, output, **kwargs)
def test_hello_world(self):
self.do_run_in_out_file_test('tests', 'core', 'test_hello_world')
src = open(self.in_dir('src.cpp.o.js')).read()
assert 'EMSCRIPTEN_GENERATED_FUNCTIONS' not in src, 'must not emit this unneeded internal thing'
def test_intvars(self):
self.do_run_in_out_file_test('tests', 'core', 'test_intvars')
def test_sintvars(self):
self.do_run_in_out_file_test('tests', 'core', 'test_sintvars',
force_c=True)
def test_i64(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64')
def test_i64_2(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64_2')
def test_i64_3(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64_3')
def test_i64_4(self):
# stuff that also needs sign corrections
self.do_run_in_out_file_test('tests', 'core', 'test_i64_4')
def test_i64_b(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64_b')
def test_i64_cmp(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64_cmp')
def test_i64_cmp2(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64_cmp2')
def test_i64_double(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64_double')
def test_i64_umul(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64_umul')
def test_i64_precise(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64_precise')
def test_i64_precise_unneeded(self):
# Verify that even if we ask for precision, if it is not needed it is not included
self.set_setting('PRECISE_I64_MATH', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_i64_precise_unneeded')
code = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read()
assert 'goog.math.Long' not in code, 'i64 precise math should never be included, musl does its own printfing'
def test_i64_precise_needed(self):
# and now one where we do
self.set_setting('PRECISE_I64_MATH', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_i64_precise_needed')
def test_i64_llabs(self):
self.set_setting('PRECISE_I64_MATH', 2)
self.do_run_in_out_file_test('tests', 'core', 'test_i64_llabs')
def test_i64_zextneg(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64_zextneg')
def test_i64_7z(self):
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_i64_7z',
args=['hallo'])
def test_i64_i16(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64_i16')
def test_i64_qdouble(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64_qdouble')
def test_i64_varargs(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i64_varargs',
args='waka fleefl asdfasdfasdfasdf'
.split(' '))
def test_vararg_copy(self):
self.do_run_in_out_file_test('tests', 'va_arg', 'test_va_copy')
def test_llvm_fabs(self):
self.set_setting('PRECISE_F32', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_llvm_fabs')
def test_double_varargs(self):
self.do_run_in_out_file_test('tests', 'core', 'test_double_varargs')
def test_struct_varargs(self):
self.do_run_in_out_file_test('tests', 'core', 'test_struct_varargs')
def zzztest_nested_struct_varargs(self):
self.do_run_in_out_file_test('tests', 'core', 'test_nested_struct_varargs')
def test_i32_mul_precise(self):
self.do_run_in_out_file_test('tests', 'core', 'test_i32_mul_precise')
def test_i16_emcc_intrinsic(self):
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_i16_emcc_intrinsic')
def test_double_i64_conversion(self):
self.do_run_in_out_file_test('tests', 'core', 'test_double_i64_conversion')
def test_float32_precise(self):
self.set_setting('PRECISE_F32', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_float32_precise')
def test_negative_zero(self):
self.do_run_in_out_file_test('tests', 'core', 'test_negative_zero')
def test_line_endings(self):
self.build(open(path_from_root('tests', 'hello_world.cpp')).read(), self.get_dir(), self.in_dir('hello_world.cpp'))
def test_literal_negative_zero(self):
self.do_run_in_out_file_test('tests', 'core', 'test_literal_negative_zero')
@no_wasm_backend('test uses calls to expected js imports, rather than using llvm intrinsics directly')
def test_llvm_intrinsics(self):
# for bswap64
self.set_setting('PRECISE_I64_MATH', 2)
self.do_run_in_out_file_test('tests', 'core', 'test_llvm_intrinsics')
def test_lower_intrinsics(self):
self.emcc_args += ['-g1']
self.do_run_in_out_file_test('tests', 'core', 'test_lower_intrinsics')
# intrinsics should be lowered out
js = open('src.cpp.o.js').read()
assert ('llvm_' not in js) == is_optimizing(self.emcc_args), 'intrinsics must be lowered when optimizing'
def test_bswap64(self):
test_path = path_from_root('tests', 'core', 'test_bswap64')
src, output = (test_path + s for s in ('.c', '.out'))
# extra coverages
for emulate_casts in [0, 1]:
for emulate_fps in [0, 1, 2]:
print(emulate_casts, emulate_fps)
self.set_setting('EMULATE_FUNCTION_POINTER_CASTS', emulate_casts)
self.set_setting('EMULATED_FUNCTION_POINTERS', emulate_fps)
self.do_run_from_file(src, output)
def test_sha1(self):
self.do_run(open(path_from_root('tests', 'sha1.c')).read(), 'SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6')
@no_wasm_backend('test checks that __asmjs__ is #defined')
def test_asmjs_unknown_emscripten(self):
# No other configuration is supported, so always run this.
self.do_run(open(path_from_root('tests', 'asmjs-unknown-emscripten.c')).read(), '')
def test_cube2md5(self):
self.emcc_args += ['--embed-file', 'cube2md5.txt']
shutil.copyfile(path_from_root('tests', 'cube2md5.txt'), os.path.join(self.get_dir(), 'cube2md5.txt'))
self.do_run(open(path_from_root('tests', 'cube2md5.cpp')).read(), open(path_from_root('tests', 'cube2md5.ok')).read())
def test_cube2hash(self):
# A good test of i64 math
self.do_run('', 'Usage: hashstring <seed>',
libraries=self.get_library('cube2hash', ['cube2hash.bc'], configure=None),
includes=[path_from_root('tests', 'cube2hash')])
for text, output in [('fleefl', '892BDB6FD3F62E863D63DA55851700FDE3ACF30204798CE9'),
('fleefl2', 'AA2CC5F96FC9D540CA24FDAF1F71E2942753DB83E8A81B61'),
('64bitisslow', '64D8470573635EC354FEE7B7F87C566FCAF1EFB491041670')]:
self.do_run('', 'hash value: ' + output, [text], no_build=True)
def test_unaligned(self):
self.skipTest('LLVM marks the reads of s as fully aligned, making this test invalid')
src = r'''
#include <stdio.h>
struct S {
double x;
int y;
};
int main() {
// the 64-bit value here will not be 8-byte aligned
S s0[3] = { {0x12a751f430142, 22}, {0x17a5c85bad144, 98}, {1, 1}};
char buffer[10*sizeof(S)];
int b = int(buffer);
S *s = (S*)(b + 4-b%8);
s[0] = s0[0];
s[1] = s0[1];
s[2] = s0[2];
printf("*%d : %d : %d\n", sizeof(S), ((unsigned int)&s[0]) % 8 != ((unsigned int)&s[1]) % 8,
((unsigned int)&s[1]) - ((unsigned int)&s[0]));
s[0].x++;
s[0].y++;
s[1].x++;
s[1].y++;
printf("%.1f,%d,%.1f,%d\n", s[0].x, s[0].y, s[1].x, s[1].y);
return 0;
}
'''
# TODO: A version of this with int64s as well
self.do_run(src, '*12 : 1 : 12\n328157500735811.0,23,416012775903557.0,99\n')
return # TODO: continue to the next part here
# Test for undefined behavior in C. This is not legitimate code, but does exist
src = r'''
#include <stdio.h>
int main()
{
int x[10];
char *p = (char*)&x[0];
p++;
short *q = (short*)p;
*q = 300;
printf("*%d:%d*\n", *q, ((int)q)%2);
int *r = (int*)p;
*r = 515559;
printf("*%d*\n", *r);
long long *t = (long long*)p;
*t = 42949672960;
printf("*%lld*\n", *t);
return 0;
}
'''
try:
self.do_run(src, '*300:1*\n*515559*\n*42949672960*\n')
except Exception as e:
assert 'must be aligned' in str(e), e # expected to fail without emulation
def test_align64(self):
src = r'''
#include <stdio.h>
// inspired by poppler
enum Type {
A = 10,
B = 20
};
struct Object {
Type type;
union {
int intg;
double real;
char *name;
};
};
struct Principal {
double x;
Object a;
double y;
};
int main(int argc, char **argv)
{
int base = argc-1;
Object *o = NULL;
printf("%d,%d\n", sizeof(Object), sizeof(Principal));
printf("%d,%d,%d,%d\n", (int)&o[base].type, (int)&o[base].intg, (int)&o[base].real, (int)&o[base].name);
printf("%d,%d,%d,%d\n", (int)&o[base+1].type, (int)&o[base+1].intg, (int)&o[base+1].real, (int)&o[base+1].name);
Principal p, q;
p.x = p.y = q.x = q.y = 0;
p.a.type = A;
p.a.real = 123.456;
*(&q.a) = p.a;
printf("%.2f,%d,%.2f,%.2f : %.2f,%d,%.2f,%.2f\n", p.x, p.a.type, p.a.real, p.y, q.x, q.a.type, q.a.real, q.y);
return 0;
}
'''
self.do_run(src, '''16,32
0,8,8,8
16,24,24,24
0.00,10,123.46,0.00 : 0.00,10,123.46,0.00
''')
def test_align_moar(self):
self.emcc_args = self.emcc_args + ['-msse']
def test():
self.do_run(r'''
#include <xmmintrin.h>
#include <stdio.h>
struct __attribute__((aligned(16))) float4x4
{
union
{
float v[4][4];
__m128 row[4];
};
};
float4x4 v;
__m128 m;
int main()
{
printf("Alignment: %d\n", ((int)&v) % 16);
printf("Alignment: %d\n", ((int)&m) % 16);
}
''', 'Alignment: 0\nAlignment: 0\n')
test()
print('relocatable')
self.set_setting('RELOCATABLE', 1)
test()
def test_aligned_alloc(self):
self.do_run(open(path_from_root('tests', 'test_aligned_alloc.c')).read(), '', assert_returncode=0)
def test_unsigned(self):
src = '''
#include <stdio.h>
const signed char cvals[2] = { -1, -2 }; // compiler can store this is a string, so -1 becomes \FF, and needs re-signing
int main()
{
{
unsigned char x = 200;
printf("*%d*\\n", x);
unsigned char y = -22;
printf("*%d*\\n", y);
}
int varey = 100;
unsigned int MAXEY = -1, MAXEY2 = -77;
printf("*%u,%d,%u*\\n", MAXEY, varey >= MAXEY, MAXEY2); // 100 >= -1? not in unsigned!
int y = cvals[0];
printf("*%d,%d,%d,%d*\\n", cvals[0], cvals[0] < 0, y, y < 0);
y = cvals[1];
printf("*%d,%d,%d,%d*\\n", cvals[1], cvals[1] < 0, y, y < 0);
// zext issue - see mathop in jsifier
unsigned char x8 = -10;
unsigned long hold = 0;
hold += x8;
int y32 = hold+50;
printf("*%u,%u*\\n", hold, y32);
// Comparisons
x8 = 0;
for (int i = 0; i < 254; i++) x8++; // make it an actual 254 in JS - not a -2
printf("*%d,%d*\\n", x8+1 == 0xff, x8+1 != 0xff); // 0xff may be '-1' in the bitcode
return 0;
}
'''
self.do_run(src, '*4294967295,0,4294967219*\n*-1,1,-1,1*\n*-2,1,-2,1*\n*246,296*\n*1,0*')
self.emcc_args.append('-Wno-constant-conversion')
src = '''
#include <stdio.h>
int main()
{
{
unsigned char x;
unsigned char *y = &x;
*y = -1;
printf("*%d*\\n", x);
}
{
unsigned short x;
unsigned short *y = &x;
*y = -1;
printf("*%d*\\n", x);
}
/*{ // This case is not checked. The hint for unsignedness is just the %u in printf, and we do not analyze that
unsigned int x;
unsigned int *y = &x;
*y = -1;
printf("*%u*\\n", x);
}*/
{
char x;
char *y = &x;
*y = 255;
printf("*%d*\\n", x);
}
{
char x;
char *y = &x;
*y = 65535;
printf("*%d*\\n", x);
}
{
char x;
char *y = &x;
*y = 0xffffffff;
printf("*%d*\\n", x);
}
return 0;
}
'''
self.do_run(src, '*255*\n*65535*\n*-1*\n*-1*\n*-1*')
def test_bitfields(self):
self.do_run_in_out_file_test('tests', 'core', 'test_bitfields')
def test_floatvars(self):
self.do_run_in_out_file_test('tests', 'core', 'test_floatvars')
def test_closebitcasts(self):
self.do_run_in_out_file_test('tests', 'core', 'closebitcasts')
def test_fast_math(self):
Building.COMPILER_TEST_OPTS += ['-ffast-math']
self.do_run_in_out_file_test('tests', 'core', 'test_fast_math',
args=['5', '6', '8'])
def test_zerodiv(self):
self.do_run_in_out_file_test('tests', 'core', 'test_zerodiv')
def test_zero_multiplication(self):
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_zero_multiplication')
def test_isnan(self):
self.do_run_in_out_file_test('tests', 'core', 'test_isnan')
def test_globaldoubles(self):
self.do_run_in_out_file_test('tests', 'core', 'test_globaldoubles')
def test_math(self):
self.do_run_in_out_file_test('tests', 'core', 'test_math')
def test_erf(self):
self.do_run_in_out_file_test('tests', 'core', 'test_erf')
def test_math_hyperbolic(self):
src = open(path_from_root('tests', 'hyperbolic', 'src.c'), 'r').read()
expected = open(path_from_root('tests', 'hyperbolic', 'output.txt'), 'r').read()
self.do_run(src, expected)
def test_math_lgamma(self):
test_path = path_from_root('tests', 'math', 'lgamma')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output)
if self.get_setting('ALLOW_MEMORY_GROWTH') == 0 and not self.is_wasm():
print('main module')
self.set_setting('MAIN_MODULE', 1)
self.do_run_from_file(src, output)
# Test that fmodf with -s PRECISE_F32=1 properly validates as asm.js (% operator cannot take in f32, only f64)
def test_math_fmodf(self):
test_path = path_from_root('tests', 'math', 'fmodf')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output)
def test_frexp(self):
self.do_run_in_out_file_test('tests', 'core', 'test_frexp')
def test_rounding(self):
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
for precise_f32 in [0, 1]:
print(precise_f32)
self.set_setting('PRECISE_F32', precise_f32)
self.do_run_in_out_file_test('tests', 'core', 'test_rounding')
def test_fcvt(self):
self.do_run_in_out_file_test('tests', 'core', 'test_fcvt')
def test_llrint(self):
self.do_run_in_out_file_test('tests', 'core', 'test_llrint')
def test_getgep(self):
# Generated code includes getelementptr (getelementptr, 0, 1), i.e., GEP as the first param to GEP
self.do_run_in_out_file_test('tests', 'core', 'test_getgep')
def test_multiply_defined_symbols(self):
a1 = "int f() { return 1; }"
a1_name = os.path.join(self.get_dir(), 'a1.c')
open(a1_name, 'w').write(a1)
a2 = "void x() {}"
a2_name = os.path.join(self.get_dir(), 'a2.c')
open(a2_name, 'w').write(a2)
b1 = "int f() { return 2; }"
b1_name = os.path.join(self.get_dir(), 'b1.c')
open(b1_name, 'w').write(b1)
b2 = "void y() {}"
b2_name = os.path.join(self.get_dir(), 'b2.c')
open(b2_name, 'w').write(b2)
main = r'''
#include <stdio.h>
int f();
int main() {
printf("result: %d\n", f());
return 0;
}
'''
main_name = os.path.join(self.get_dir(), 'main.c')
open(main_name, 'w').write(main)
Building.emcc(a1_name)
Building.emcc(a2_name)
Building.emcc(b1_name)
Building.emcc(b2_name)
Building.emcc(main_name)
liba_name = os.path.join(self.get_dir(), 'liba.a')
Building.emar('cr', liba_name, [a1_name + '.o', a2_name + '.o'])
libb_name = os.path.join(self.get_dir(), 'libb.a')
Building.emar('cr', libb_name, [b1_name + '.o', b2_name + '.o'])
all_name = os.path.join(self.get_dir(), 'all.bc')
Building.link([main_name + '.o', liba_name, libb_name], all_name)
self.do_ll_run(all_name, 'result: 1')
def test_if(self):
self.do_run_in_out_file_test('tests', 'core', 'test_if')
def test_if_else(self):
self.do_run_in_out_file_test('tests', 'core', 'test_if_else')
def test_loop(self):
self.do_run_in_out_file_test('tests', 'core', 'test_loop')
def test_stack(self):
self.set_setting('INLINING_LIMIT', 50)
self.do_run_in_out_file_test('tests', 'core', 'test_stack')
def test_stack_align(self):
src = path_from_root('tests', 'core', 'test_stack_align.cpp')
def test():
self.do_run(open(src).read(), ['''align 4: 0
align 8: 0
align 16: 0
align 32: 0
base align: 0, 0, 0, 0'''])
test()
if '-O' in str(self.emcc_args):
print('outlining')
self.set_setting('OUTLINING_LIMIT', 60)
test()
@no_emterpreter
def test_stack_restore(self):
if self.is_wasm():
self.skipTest('generated code not available in wasm')
self.emcc_args += ['-g3'] # to be able to find the generated code
test_path = path_from_root('tests', 'core', 'test_stack_restore')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output)
generated = open('src.cpp.o.js').read()
def ensure_stack_restore_count(function_name, expected_count):
code = generated[generated.find(function_name):]
code = code[:code.find('\n}') + 2]
actual_count = code.count('STACKTOP = sp')
assert actual_count == expected_count, ('Expected %d stack restorations, got %d' % (expected_count, actual_count)) + ': ' + code
ensure_stack_restore_count('function _no_stack_usage', 0)
ensure_stack_restore_count('function _alloca_gets_restored', 1)
ensure_stack_restore_count('function _stack_usage', 1)
def test_strings(self):
test_path = path_from_root('tests', 'core', 'test_strings')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output, ['wowie', 'too', '74'])
def test_strcmp_uni(self):
self.do_run_in_out_file_test('tests', 'core', 'test_strcmp_uni')
def test_strndup(self):
self.do_run_in_out_file_test('tests', 'core', 'test_strndup')
def test_errar(self):
self.do_run_in_out_file_test('tests', 'core', 'test_errar')
def test_mainenv(self):
self.do_run_in_out_file_test('tests', 'core', 'test_mainenv')
def test_funcs(self):
self.do_run_in_out_file_test('tests', 'core', 'test_funcs')
def test_structs(self):
test_path = path_from_root('tests', 'core', 'test_structs')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output)
gen_struct_src = '''
#include <stdio.h>
#include <stdlib.h>
#include "emscripten.h"
struct S
{
int x, y;
};
int main()
{
S* a = {{gen_struct}};
a->x = 51; a->y = 62;
printf("*%d,%d*\\n", a->x, a->y);
{{del_struct}}(a);
return 0;
}
'''
def test_mallocstruct(self):
self.do_run(self.gen_struct_src.replace('{{gen_struct}}', '(S*)malloc(sizeof(S))').replace('{{del_struct}}', 'free'), '*51,62*')
def test_emmalloc(self):
# in newer clang+llvm, the internal calls to malloc in emmalloc may be optimized under
# the assumption that they are external, so like in system_libs.py where we build
# malloc, we need to disable builtin here too
self.emcc_args += ['-fno-builtin']
def test():
self.do_run(open(path_from_root('system', 'lib', 'emmalloc.cpp')).read() + open(path_from_root('tests', 'core', 'test_emmalloc.cpp')).read(),
open(path_from_root('tests', 'core', 'test_emmalloc.txt')).read())
print('normal')
test()
print('debug')
self.emcc_args += ['-DEMMALLOC_DEBUG']
test()
print('debug log')
self.emcc_args += ['-DEMMALLOC_DEBUG_LOG']
self.emcc_args += ['-DRANDOM_ITERS=130']
test()
def test_newstruct(self):
self.do_run(self.gen_struct_src.replace('{{gen_struct}}', 'new S').replace('{{del_struct}}', 'delete'), '*51,62*')
def test_addr_of_stacked(self):
self.do_run_in_out_file_test('tests', 'core', 'test_addr_of_stacked')
def test_globals(self):
self.do_run_in_out_file_test('tests', 'core', 'test_globals')
def test_linked_list(self):
self.do_run_in_out_file_test('tests', 'core', 'test_linked_list')
def test_sup(self):
src = '''
#include <stdio.h>
struct S4 { int x; }; // size: 4
struct S4_2 { short x, y; }; // size: 4, but for alignment purposes, 2
struct S6 { short x, y, z; }; // size: 6
struct S6w { char x[6]; }; // size: 6 also
struct S6z { int x; short y; }; // size: 8, since we align to a multiple of the biggest - 4
struct C___ { S6 a, b, c; int later; };
struct Carr { S6 a[3]; int later; }; // essentially the same, but differently defined
struct C__w { S6 a; S6w b; S6 c; int later; }; // same size, different struct
struct Cp1_ { int pre; short a; S6 b, c; int later; }; // fillers for a
struct Cp2_ { int a; short pre; S6 b, c; int later; }; // fillers for a (get addr of the other filler)
struct Cint { S6 a; int b; S6 c; int later; }; // An int (different size) for b
struct C4__ { S6 a; S4 b; S6 c; int later; }; // Same size as int from before, but a struct
struct C4_2 { S6 a; S4_2 b; S6 c; int later; }; // Same size as int from before, but a struct with max element size 2
struct C__z { S6 a; S6z b; S6 c; int later; }; // different size, 8 instead of 6
int main()
{
#define TEST(struc) \\
{ \\
struc *s = 0; \\
printf("*%s: %d,%d,%d,%d<%d*\\n", #struc, (int)&(s->a), (int)&(s->b), (int)&(s->c), (int)&(s->later), sizeof(struc)); \\
}
#define TEST_ARR(struc) \\
{ \\
struc *s = 0; \\
printf("*%s: %d,%d,%d,%d<%d*\\n", #struc, (int)&(s->a[0]), (int)&(s->a[1]), (int)&(s->a[2]), (int)&(s->later), sizeof(struc)); \\
}
printf("sizeofs:%d,%d\\n", sizeof(S6), sizeof(S6z));
TEST(C___);
TEST_ARR(Carr);
TEST(C__w);
TEST(Cp1_);
TEST(Cp2_);
TEST(Cint);
TEST(C4__);
TEST(C4_2);
TEST(C__z);
return 0;
}
'''
self.do_run(src, 'sizeofs:6,8\n*C___: 0,6,12,20<24*\n*Carr: 0,6,12,20<24*\n*C__w: 0,6,12,20<24*\n*Cp1_: 4,6,12,20<24*\n*Cp2_: 0,6,12,20<24*\n*Cint: 0,8,12,20<24*\n*C4__: 0,8,12,20<24*\n*C4_2: 0,6,10,16<20*\n*C__z: 0,8,16,24<28*')
def test_assert(self):
self.do_run_in_out_file_test('tests', 'core', 'test_assert')
def test_libcextra(self):
self.do_run_in_out_file_test('tests', 'core', 'test_libcextra')
def test_regex(self):
self.do_run_in_out_file_test('tests', 'core', 'test_regex')
def test_longjmp(self):
self.do_run_in_out_file_test('tests', 'core', 'test_longjmp')
def test_longjmp2(self):
self.do_run_in_out_file_test('tests', 'core', 'test_longjmp2')
def test_longjmp3(self):
self.do_run_in_out_file_test('tests', 'core', 'test_longjmp3')
def test_longjmp4(self):
self.do_run_in_out_file_test('tests', 'core', 'test_longjmp4')
def test_longjmp_funcptr(self):
self.do_run_in_out_file_test('tests', 'core', 'test_longjmp_funcptr')
def test_longjmp_repeat(self):
self.do_run_in_out_file_test('tests', 'core', 'test_longjmp_repeat')
def test_longjmp_stacked(self):
self.do_run_in_out_file_test('tests', 'core', 'test_longjmp_stacked')
def test_longjmp_exc(self):
self.do_run_in_out_file_test('tests', 'core', 'test_longjmp_exc')
def test_longjmp_throw(self):
for disable_throw in [0, 1]:
print(disable_throw)
self.set_setting('DISABLE_EXCEPTION_CATCHING', disable_throw)
self.do_run_in_out_file_test('tests', 'core', 'test_longjmp_throw')
def test_longjmp_unwind(self):
self.do_run_in_out_file_test('tests', 'core', 'test_longjmp_unwind')
def test_siglongjmp(self):
self.do_run_in_out_file_test('tests', 'core', 'test_siglongjmp')
def test_setjmp_many(self):
src = r'''
#include <stdio.h>
#include <setjmp.h>
int main(int argc, char** argv) {
jmp_buf buf;
for (int i = 0; i < NUM; i++) printf("%d\n", setjmp(buf));
if (argc-- == 1131) longjmp(buf, 11);
return 0;
}
'''
for num in [1, 5, 20, 1000]:
print('NUM=%d' % num)
self.do_run(src.replace('NUM', str(num)), '0\n' * num)
def test_setjmp_many_2(self):
src = r'''
#include <setjmp.h>
#include <stdio.h>
jmp_buf env;
void luaWork(int d){
int x;
printf("d is at %d\n", d);
longjmp(env, 1);
}
int main()
{
const int ITERATIONS=25;
for(int i = 0; i < ITERATIONS; i++){
if(!setjmp(env)){
luaWork(i);
}
}
return 0;
}
'''
self.do_run(src, r'''d is at 24''')
def test_setjmp_noleak(self):
src = r'''
#include <setjmp.h>
#include <stdio.h>
#include <assert.h>
jmp_buf env;
void luaWork(int d){
int x;
printf("d is at %d\n", d);
longjmp(env, 1);
}
#include <malloc.h>
#include <stdlib.h>
void dump() {
struct mallinfo m = mallinfo();
printf("dump: %d , %d\n", m.arena, m.uordblks);
}
void work(int n)
{
printf("work %d\n", n);
dump();
if(!setjmp(env)){
luaWork(n);
}
if (n > 0) work(n-1);
}
int main() {
struct mallinfo m1 = mallinfo();
dump();
work(10);
dump();
struct mallinfo m2 = mallinfo();
assert(m1.uordblks == m2.uordblks);
printf("ok.\n");
}
'''
self.do_run(src, r'''ok.''')
def test_exceptions(self):
self.set_setting('EXCEPTION_DEBUG', 1)
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.maybe_closure()
src = '''
#include <stdio.h>
void thrower() {
printf("infunc...");
throw(99);
printf("FAIL");
}
int main() {
try {
printf("*throw...");
throw(1);
printf("FAIL");
} catch(...) {
printf("caught!");
}
try {
thrower();
} catch(...) {
printf("done!*\\n");
}
return 0;
}
'''
self.do_run(src, '*throw...caught!infunc...done!*')
self.set_setting('DISABLE_EXCEPTION_CATCHING', 1)
self.do_run(src, 'Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0')
def test_exceptions_custom(self):
self.set_setting('EXCEPTION_DEBUG', 1)
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.maybe_closure()
src = '''
#include <iostream>
class MyException
{
public:
MyException(){ std::cout << "Construct..."; }
MyException( const MyException & ) { std::cout << "Copy..."; }
~MyException(){ std::cout << "Destruct..."; }
};
int function()
{
std::cout << "Throw...";
throw MyException();
}
int function2()
{
return function();
}
int main()
{
try
{
function2();
}
catch (MyException & e)
{
std::cout << "Caught...";
}
try
{
function2();
}
catch (MyException e)
{
std::cout << "Caught...";
}
return 0;
}
'''
self.do_run(src, 'Throw...Construct...Caught...Destruct...Throw...Construct...Copy...Caught...Destruct...Destruct...')
def test_exceptions_2(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
for safe in [0, 1]:
print(safe)
self.set_setting('SAFE_HEAP', safe)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_2')
def test_exceptions_3(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
src = r'''
#include <iostream>
#include <stdexcept>
int main(int argc, char **argv)
{
if (argc != 2) {
std::cout << "need an arg" << std::endl;
return 1;
}
int arg = argv[1][0] - '0';
try {
if (arg == 0) throw "a c string";
if (arg == 1) throw std::exception();
if (arg == 2) throw std::runtime_error("Hello");
} catch(const char * ex) {
std::cout << "Caught C string: " << ex << std::endl;
} catch(const std::exception &ex) {
std::cout << "Caught exception: " << ex.what() << std::endl;
} catch(...) {
std::cout << "Caught something else" << std::endl;
}
std::cout << "Done.\n";
}
'''
print('0')
self.do_run(src, 'Caught C string: a c string\nDone.', ['0'])
print('1')
self.do_run(src, 'Caught exception: std::exception\nDone.', ['1'], no_build=True)
print('2')
self.do_run(src, 'Caught exception: Hello\nDone.', ['2'], no_build=True)
def test_exceptions_white_list(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 2)
# Wasm does not add an underscore to function names. For wasm, the
# mismatches are fixed in fixImports() function in JS glue code.
if not self.is_wasm_backend():
self.set_setting('EXCEPTION_CATCHING_WHITELIST', ["__Z12somefunctionv"])
else:
self.set_setting('EXCEPTION_CATCHING_WHITELIST', ["_Z12somefunctionv"])
# otherwise it is inlined and not identified
self.set_setting('INLINING_LIMIT', 50)
test_path = path_from_root('tests', 'core', 'test_exceptions_white_list')
src, output = (test_path + s for s in ('.cpp', '.out'))
self.do_run_from_file(src, output)
size = len(open('src.cpp.o.js').read())
shutil.copyfile('src.cpp.o.js', 'orig.js')
# check that an empty whitelist works properly (as in, same as exceptions disabled)
empty_output = path_from_root('tests', 'core', 'test_exceptions_white_list_empty.out')
self.set_setting('EXCEPTION_CATCHING_WHITELIST', [])
self.do_run_from_file(src, empty_output)
empty_size = len(open('src.cpp.o.js').read())
shutil.copyfile('src.cpp.o.js', 'empty.js')
self.set_setting('EXCEPTION_CATCHING_WHITELIST', ['fake'])
self.do_run_from_file(src, empty_output)
fake_size = len(open('src.cpp.o.js').read())
shutil.copyfile('src.cpp.o.js', 'fake.js')
self.set_setting('DISABLE_EXCEPTION_CATCHING', 1)
self.do_run_from_file(src, empty_output)
disabled_size = len(open('src.cpp.o.js').read())
shutil.copyfile('src.cpp.o.js', 'disabled.js')
if not self.is_wasm():
print(size, empty_size, fake_size, disabled_size)
assert size - empty_size > 0.0025 * size, [empty_size, size] # big change when we disable entirely
assert size - fake_size > 0.0025 * size, [fake_size, size]
assert abs(empty_size - fake_size) < 0.007 * size, [empty_size, fake_size]
assert empty_size - disabled_size < 0.007 * size, [empty_size, disabled_size] # full disable removes a little bit more
assert fake_size - disabled_size < 0.007 * size, [disabled_size, fake_size]
def test_exceptions_white_list_2(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 2)
# Wasm does not add an underscore to function names. For wasm, the
# mismatches are fixed in fixImports() function in JS glue code.
if not self.is_wasm_backend():
self.set_setting('EXCEPTION_CATCHING_WHITELIST', ["_main"])
else:
self.set_setting('EXCEPTION_CATCHING_WHITELIST', ["main"])
# otherwise it is inlined and not identified
self.set_setting('INLINING_LIMIT', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_white_list_2')
def test_exceptions_uncaught(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
src = r'''
#include <stdio.h>
#include <exception>
struct X {
~X() {
printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no");
}
};
int main() {
printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no");
try {
X x;
throw 1;
} catch(...) {
printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no");
}
printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no");
return 0;
}
'''
self.do_run(src, 'exception? no\nexception? yes\nexception? no\nexception? no\n')
src = r'''
#include <fstream>
#include <iostream>
int main() {
std::ofstream os("test");
os << std::unitbuf << "foo"; // trigger a call to std::uncaught_exception from
// std::basic_ostream::sentry::~sentry
std::cout << "success";
}
'''
self.do_run(src, 'success')
def test_exceptions_uncaught_2(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
src = r'''
#include <iostream>
#include <exception>
int main() {
try {
throw std::exception();
} catch(std::exception) {
try {
throw;
} catch(std::exception) {}
}
if (std::uncaught_exception())
std::cout << "ERROR: uncaught_exception still set.";
else
std::cout << "OK";
}
'''
self.do_run(src, 'OK\n')
def test_exceptions_typed(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.emcc_args += ['-s', 'SAFE_HEAP=0'] # Throwing null will cause an ignorable null pointer access.
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_typed')
def test_exceptions_virtual_inheritance(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_virtual_inheritance')
def test_exceptions_convert(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_convert')
def test_exceptions_multi(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_multi')
def test_exceptions_std(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.emcc_args += ['-s', 'SAFE_HEAP=0']
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_std')
def test_exceptions_alias(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_alias')
def test_exceptions_rethrow(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_rethrow')
def test_exceptions_resume(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.set_setting('EXCEPTION_DEBUG', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_resume')
def test_exceptions_destroy_virtual(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_destroy_virtual')
def test_exceptions_refcount(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_refcount')
def test_exceptions_primary(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_primary')
def test_exceptions_simplify_cfg(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_simplify_cfg')
def test_exceptions_libcxx(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_libcxx')
def test_exceptions_multiple_inherit(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_multiple_inherit')
def test_bad_typeid(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.do_run(r'''
// exception example
#include <iostream> // std::cerr
#include <typeinfo> // operator typeid
#include <exception> // std::exception
class Polymorphic {virtual void member(){}};
int main () {
try
{
Polymorphic * pb = 0;
const std::type_info& ti = typeid(*pb); // throws a bad_typeid exception
}
catch (std::exception& e)
{
std::cerr << "exception caught: " << e.what() << '\n';
}
return 0;
}
''', 'exception caught: std::bad_typeid')
def test_iostream_ctors(self): # iostream stuff must be globally constructed before user global constructors, so iostream works in global constructors
self.do_run(r'''
#include <iostream>
struct A {
A() { std::cout << "bug"; }
};
A a;
int main() {
std::cout << "free code" << std::endl;
return 0;
}
''', "bugfree code")
def test_class(self):
self.do_run_in_out_file_test('tests', 'core', 'test_class')
def test_inherit(self):
self.do_run_in_out_file_test('tests', 'core', 'test_inherit')
def test_isdigit_l(self):
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_isdigit_l')
def test_iswdigit(self):
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_iswdigit')
def test_polymorph(self):
self.do_run_in_out_file_test('tests', 'core', 'test_polymorph')
def test_complex(self):
self.do_run_in_out_file_test('tests', 'core', 'test_complex', force_c=True)
def test_float_builtins(self):
# tests wasm_libc_rt
if not self.is_wasm_backend():
self.skipTest('no __builtin_fmin support in JSBackend')
self.do_run_in_out_file_test('tests', 'core', 'test_float_builtins')
def test_segfault(self):
self.set_setting('SAFE_HEAP', 1)
for addr in ['0', 'new D2()']:
print(addr)
src = r'''
#include <stdio.h>
struct Classey {
virtual void doIt() = 0;
};
struct D1 : Classey {
virtual void doIt() { printf("fleefl\n"); }
};
struct D2 : Classey {
virtual void doIt() { printf("marfoosh\n"); }
};
int main(int argc, char **argv)
{
Classey *p = argc == 100 ? new D1() : (Classey*)%s;
p->doIt();
return 0;
}
''' % addr
self.do_run(src, 'segmentation fault' if addr.isdigit() else 'marfoosh')
def test_dynamic_cast(self):
self.do_run_in_out_file_test('tests', 'core', 'test_dynamic_cast')
def test_dynamic_cast_b(self):
self.do_run_in_out_file_test('tests', 'core', 'test_dynamic_cast_b')
def test_dynamic_cast_2(self):
self.do_run_in_out_file_test('tests', 'core', 'test_dynamic_cast_2')
def test_funcptr(self):
self.do_run_in_out_file_test('tests', 'core', 'test_funcptr')
def test_mathfuncptr(self):
self.do_run_in_out_file_test('tests', 'core', 'test_mathfuncptr')
if self.is_emterpreter():
print('emterpreter f32')
self.set_setting('PRECISE_F32', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_mathfuncptr')
def test_funcptrfunc(self):
self.do_run_in_out_file_test('tests', 'core', 'test_funcptrfunc')
def test_funcptr_namecollide(self):
self.do_run_in_out_file_test('tests', 'core', 'test_funcptr_namecollide')
def test_emptyclass(self):
self.do_run_in_out_file_test('tests', 'core', 'test_emptyclass')
def test_alloca(self):
self.do_run_in_out_file_test('tests', 'core', 'test_alloca')
def test_rename(self):
self.do_run_in_out_file_test('tests', 'stdio', 'test_rename', force_c=True)
def test_remove(self):
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'cstdio', 'test_remove')
def test_alloca_stack(self):
self.do_run_in_out_file_test('tests', 'core', 'test_alloca_stack')
def test_stack_byval(self):
self.do_run_in_out_file_test('tests', 'core', 'test_stack_byval')
def test_stack_varargs(self):
self.set_setting('INLINING_LIMIT', 50)
self.set_setting('TOTAL_STACK', 2048)
self.do_run_in_out_file_test('tests', 'core', 'test_stack_varargs')
def test_stack_varargs2(self):
self.set_setting('TOTAL_STACK', 1536)
src = r'''
#include <stdio.h>
#include <stdlib.h>
void func(int i) {
}
int main() {
for (int i = 0; i < 1024; i++) {
printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i);
}
printf("ok!\n");
return 0;
}
'''
self.do_run(src, 'ok!')
print('with return')
src = r'''
#include <stdio.h>
#include <stdlib.h>
int main() {
for (int i = 0; i < 1024; i++) {
int j = printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i);
printf(" (%d)\n", j);
}
printf("ok!\n");
return 0;
}
'''
self.do_run(src, 'ok!')
print('with definitely no return')
src = r'''
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
void vary(const char *s, ...)
{
va_list v;
va_start(v, s);
char d[20];
vsnprintf(d, 20, s, v);
puts(d);
// Try it with copying
va_list tempva;
va_copy(tempva, v);
vsnprintf(d, 20, s, tempva);
puts(d);
va_end(v);
}
int main() {
for (int i = 0; i < 1024; i++) {
int j = printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i);
printf(" (%d)\n", j);
vary("*cheez: %d+%d*", 99, 24);
vary("*albeit*");
}
printf("ok!\n");
return 0;
}
'''
self.do_run(src, 'ok!')
def test_stack_void(self):
self.set_setting('INLINING_LIMIT', 50)
self.do_run_in_out_file_test('tests', 'core', 'test_stack_void')
# Fails in wasm because of excessive slowness in the wasm-shell
@no_wasm()
def test_life(self):
self.emcc_args += ['-std=c99']
src = open(path_from_root('tests', 'life.c'), 'r').read()
self.do_run(src, '''--------------------------------
[] [] [][][]
[] [] [] [][] [] [] []
[] [][] [][] [][][] []
[] [] [] [] [][] [] []
[] [][] [] [] [] [] [][][][]
[][] [][] [] [][][] [] []
[] [][] [][] [][] [][][]
[][] [][][] [] []
[][] [][] []
[][][]
[]
[][][]
[] [][] [][]
[][] [] [][] [][]
[][] [][]
[]
[][]
[][] []
[] [][] []
[][][] []
[] [][]
[] [] []
[]
[] [] []
[][][]
[]
[][][] []
--------------------------------
''', ['2'], force_c=True) # noqa
def test_array2(self):
self.do_run_in_out_file_test('tests', 'core', 'test_array2')
def test_array2b(self):
self.do_run_in_out_file_test('tests', 'core', 'test_array2b')
def test_constglobalstructs(self):
self.do_run_in_out_file_test('tests', 'core', 'test_constglobalstructs')
def test_conststructs(self):
self.do_run_in_out_file_test('tests', 'core', 'test_conststructs')
def test_bigarray(self):
self.do_run_in_out_file_test('tests', 'core', 'test_bigarray')
def test_mod_globalstruct(self):
self.do_run_in_out_file_test('tests', 'core', 'test_mod_globalstruct')
@no_wasm_backend('long doubles are f128s in wasm backend')
def test_pystruct(self):
def test():
self.do_run_in_out_file_test('tests', 'test_pystruct')
test()
print('relocatable') # this tests recursive global structs => nontrivial postSets for relocation
assert self.get_setting('RELOCATABLE') == self.get_setting('EMULATED_FUNCTION_POINTERS') == 0
self.set_setting('RELOCATABLE', 1)
self.set_setting('EMULATED_FUNCTION_POINTERS', 1)
test()
def test_sizeof(self):
# Has invalid writes between printouts
self.set_setting('SAFE_HEAP', 0)
self.do_run_in_out_file_test('tests', 'core', 'test_sizeof')
def test_llvm_used(self):
Building.LLVM_OPTS = 3
self.do_run_in_out_file_test('tests', 'core', 'test_llvm_used')
def test_set_align(self):
self.set_setting('SAFE_HEAP', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_set_align')
def test_emscripten_api(self):
check = '''
def process(filename):
src = open(filename, 'r').read()
# TODO: restore this (see comment in emscripten.h) assert '// hello from the source' in src
'''
self.set_setting('EXPORTED_FUNCTIONS', ['_main', '_save_me_aimee'])
self.do_run_in_out_file_test('tests', 'core', 'test_emscripten_api',
js_transform=check)
# test EXPORT_ALL
self.set_setting('EXPORTED_FUNCTIONS', [])
self.set_setting('EXPORT_ALL', 1)
self.set_setting('LINKABLE', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_emscripten_api',
js_transform=check)
def test_emscripten_run_script_string_utf8(self):
src = r'''
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <emscripten.h>
int main() {
const char *str = emscripten_run_script_string("'\\u2603 \\u2603 \\u2603 Hello!'");
printf("length of returned string: %d. Position of substring 'Hello': %d\n", strlen(str), strstr(str, "Hello")-str);
return 0;
}
'''
self.do_run(src, '''length of returned string: 18. Position of substring 'Hello': 12''')
def test_emscripten_get_now(self):
self.banned_js_engines = [V8_ENGINE] # timer limitations in v8 shell
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
if self.run_name == 'asm2':
self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage
self.do_run(open(path_from_root('tests', 'emscripten_get_now.cpp')).read(), 'Timer resolution is good')
def test_emscripten_get_compiler_setting(self):
test_path = path_from_root('tests', 'core', 'emscripten_get_compiler_setting')
src, output = (test_path + s for s in ('.c', '.out'))
old = self.get_setting('ASSERTIONS')
# with assertions, a nice message is shown
self.set_setting('ASSERTIONS', 1)
self.do_run(open(src).read(), 'You must build with -s RETAIN_COMPILER_SETTINGS=1')
self.set_setting('ASSERTIONS', old)
self.set_setting('RETAIN_COMPILER_SETTINGS', 1)
self.do_run(open(src).read(), open(output).read().replace('waka', shared.EMSCRIPTEN_VERSION))
# TODO: test only worked in non-fastcomp
def test_inlinejs(self):
self.skipTest('non-fastcomp is deprecated and fails in 3.5') # only supports EM_ASM
self.do_run_in_out_file_test('tests', 'core', 'test_inlinejs')
if self.emcc_args == []:
# opts will eliminate the comments
out = open('src.cpp.o.js').read()
for i in range(1, 5):
assert ('comment%d' % i) in out
# TODO: test only worked in non-fastcomp
def test_inlinejs2(self):
self.skipTest('non-fastcomp is deprecated and fails in 3.5') # only supports EM_ASM
self.do_run_in_out_file_test('tests', 'core', 'test_inlinejs2')
def test_inlinejs3(self):
if self.is_wasm():
self.skipTest('wasm requires a proper asm module')
test_path = path_from_root('tests', 'core', 'test_inlinejs3')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output)
print('no debugger, check validation')
src = open(src).read().replace('emscripten_debugger();', '')
self.do_run(src, open(output).read())
def test_inlinejs4(self):
self.do_run(r'''
#include <emscripten.h>
#define TO_STRING_INNER(x) #x
#define TO_STRING(x) TO_STRING_INNER(x)
#define assert_msg(msg, file, line) EM_ASM( throw 'Assert (' + msg + ') failed in ' + file + ':' + line + '!'; )
#define assert(expr) { \
if (!(expr)) { \
assert_msg(#expr, TO_STRING(__FILE__), TO_STRING(__LINE__)); \
} \
}
int main(int argc, char **argv) {
assert(argc != 17);
assert(false);
return 0;
}
''', 'false')
def test_em_asm(self):
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm')
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm', force_c=True)
# Tests various different ways to invoke the EM_ASM(), EM_ASM_INT() and EM_ASM_DOUBLE() macros.
def test_em_asm_2(self):
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm_2')
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm_2', force_c=True)
# Tests various different ways to invoke the MAIN_THREAD_EM_ASM(), MAIN_THREAD_EM_ASM_INT() and MAIN_THREAD_EM_ASM_DOUBLE() macros.
# This test is identical to test_em_asm_2, just search-replaces EM_ASM to MAIN_THREAD_EM_ASM on the test file. That way if new
# test cases are added to test_em_asm_2.cpp for EM_ASM, they will also get tested in MAIN_THREAD_EM_ASM form.
@no_wasm_backend('Proxying EM_ASM calls is not yet implemented in Wasm backend')
def test_main_thread_em_asm(self):
self.skipTest('TODO: Enable me when we have tagged new compiler build')
src = open(path_from_root('tests', 'core', 'test_em_asm_2.cpp'), 'r').read()
test_file = 'src.cpp'
open(test_file, 'w').write(src.replace('EM_ASM', 'MAIN_THREAD_EM_ASM'))
expected_result = open(path_from_root('tests', 'core', 'test_em_asm_2.out'), 'r').read()
expected_result_file = 'result.out'
open(expected_result_file, 'w').write(expected_result.replace('EM_ASM', 'MAIN_THREAD_EM_ASM'))
self.do_run_from_file(test_file, expected_result_file)
self.do_run_from_file(test_file, expected_result_file, force_c=True)
@no_wasm_backend('Proxying EM_ASM calls is not yet implemented in Wasm backend')
def test_main_thread_async_em_asm(self):
self.do_run_in_out_file_test('tests', 'core', 'test_main_thread_async_em_asm')
self.do_run_in_out_file_test('tests', 'core', 'test_main_thread_async_em_asm', force_c=True)
def test_em_asm_unicode(self):
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm_unicode')
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm_unicode', force_c=True)
def test_em_asm_unused_arguments(self):
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm_unused_arguments')
# Verify that EM_ASM macros support getting called with multiple arities.
# Maybe tests will later be joined into larger compilation units?
# Then this must still be compiled separately from other code using EM_ASM
# macros with arities 1-3. Otherwise this may incorrectly report a success.
def test_em_asm_parameter_pack(self):
Building.COMPILER_TEST_OPTS += ['-std=c++11']
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm_parameter_pack')
def test_em_js(self):
self.do_run_in_out_file_test('tests', 'core', 'test_em_js')
self.do_run_in_out_file_test('tests', 'core', 'test_em_js', force_c=True)
def test_runtime_stacksave(self):
src = open(path_from_root('tests', 'core', 'test_runtime_stacksave.c')).read()
self.do_run(src, 'success')
def test_memorygrowth(self):
self.emcc_args += ['-s', 'ALLOW_MEMORY_GROWTH=0'] # start with 0
# With typed arrays in particular, it is dangerous to use more memory than TOTAL_MEMORY,
# since we then need to enlarge the heap(s).
src = open(path_from_root('tests', 'core', 'test_memorygrowth.c')).read()
# Fail without memory growth
self.do_run(src, 'Cannot enlarge memory arrays.')
fail = open('src.cpp.o.js').read()
# Win with it
self.emcc_args += ['-s', 'ALLOW_MEMORY_GROWTH=1']
self.do_run(src, '*pre: hello,4.955*\n*hello,4.955*\n*hello,4.955*')
win = open('src.cpp.o.js').read()
if '-O2' in self.emcc_args and not self.is_wasm():
# Make sure ALLOW_MEMORY_GROWTH generates different code (should be less optimized)
possible_starts = ['// EMSCRIPTEN_START_FUNCS', 'var TOTAL_MEMORY']
code_start = None
for s in possible_starts:
if fail.find(s) >= 0:
code_start = s
break
assert code_start is not None, 'Generated code must contain one of ' + str(possible_starts)
fail = fail[fail.find(code_start):]
win = win[win.find(code_start):]
assert len(fail) < len(win), 'failing code - without memory growth on - is more optimized, and smaller' + str([len(fail), len(win)])
# Tracing of memory growths should work
self.set_setting('EMSCRIPTEN_TRACING', 1)
self.emcc_args += ['--tracing']
self.do_run(src, '*pre: hello,4.955*\n*hello,4.955*\n*hello,4.955*')
def test_memorygrowth_2(self):
self.emcc_args += ['-s', 'ALLOW_MEMORY_GROWTH=0'] # start with 0
emcc_args = self.emcc_args[:]
def test():
self.emcc_args = emcc_args[:]
# With typed arrays in particular, it is dangerous to use more memory than TOTAL_MEMORY,
# since we then need to enlarge the heap(s).
src = open(path_from_root('tests', 'core', 'test_memorygrowth_2.c')).read()
# Fail without memory growth
self.do_run(src, 'Cannot enlarge memory arrays.')
fail = open('src.cpp.o.js').read()
# Win with it
self.emcc_args += ['-s', 'ALLOW_MEMORY_GROWTH=1']
self.do_run(src, '*pre: hello,4.955*\n*hello,4.955*\n*hello,4.955*')
win = open('src.cpp.o.js').read()
if '-O2' in self.emcc_args and not self.is_wasm():
# Make sure ALLOW_MEMORY_GROWTH generates different code (should be less optimized)
code_start = 'var TOTAL_MEMORY'
fail = fail[fail.find(code_start):]
win = win[win.find(code_start):]
assert len(fail) < len(win), 'failing code - without memory growth on - is more optimized, and smaller' + str([len(fail), len(win)])
test()
if not self.is_wasm():
print('split memory')
self.set_setting('SPLIT_MEMORY', 16 * 1024 * 1024)
test()
self.set_setting('SPLIT_MEMORY', 0)
def test_memorygrowth_3(self):
# checks handling of malloc failure properly
self.emcc_args += ['-s', 'ALLOW_MEMORY_GROWTH=0', '-s', 'ABORTING_MALLOC=0', '-s', 'SAFE_HEAP=1']
self.do_run_in_out_file_test('tests', 'core', 'test_memorygrowth_3')
def test_memorygrowth_wasm_mem_max(self):
if not self.is_wasm():
self.skipTest('wasm memory specific test')
# check that memory growth does not exceed the wasm mem max limit
self.emcc_args += ['-s', 'ALLOW_MEMORY_GROWTH=1', '-s', 'TOTAL_MEMORY=64Mb', '-s', 'WASM_MEM_MAX=100Mb']
self.do_run_in_out_file_test('tests', 'core', 'test_memorygrowth_wasm_mem_max')
def test_memorygrowth_3_force_fail_reallocBuffer(self):
self.emcc_args += ['-s', 'ALLOW_MEMORY_GROWTH=1', '-DFAIL_REALLOC_BUFFER']
self.do_run_in_out_file_test('tests', 'core', 'test_memorygrowth_3')
def test_ssr(self): # struct self-ref
src = '''
#include <stdio.h>
// see related things in openjpeg
typedef struct opj_mqc_state {
unsigned int qeval;
int mps;
struct opj_mqc_state *nmps;
struct opj_mqc_state *nlps;
} opj_mqc_state_t;
static opj_mqc_state_t mqc_states[4] = {
{0x5600, 0, &mqc_states[2], &mqc_states[3]},
{0x5602, 1, &mqc_states[3], &mqc_states[2]},
};
int main() {
printf("*%d*\\n", (int)(mqc_states+1)-(int)mqc_states);
for (int i = 0; i < 2; i++)
printf("%d:%d,%d,%d,%d\\n", i, mqc_states[i].qeval, mqc_states[i].mps,
(int)mqc_states[i].nmps-(int)mqc_states, (int)mqc_states[i].nlps-(int)mqc_states);
return 0;
}
'''
self.do_run(src, '''*16*\n0:22016,0,32,48\n1:22018,1,48,32\n''')
def test_tinyfuncstr(self):
self.do_run_in_out_file_test('tests', 'core', 'test_tinyfuncstr')
def test_llvmswitch(self):
self.do_run_in_out_file_test('tests', 'core', 'test_llvmswitch')
# By default, when user has not specified a -std flag, Emscripten should always build .cpp files using the C++03 standard,
# i.e. as if "-std=c++03" had been passed on the command line. On Linux with Clang 3.2 this is the case, but on Windows
# with Clang 3.2 -std=c++11 has been chosen as default, because of
# < jrose> clb: it's deliberate, with the idea that for people who don't care about the standard, they should be using the "best" thing we can offer on that platform
def test_cxx03_do_run(self):
self.do_run_in_out_file_test('tests', 'core', 'test_cxx03_do_run')
@no_emterpreter
def test_bigswitch(self):
src = open(path_from_root('tests', 'bigswitch.cpp')).read()
self.do_run(src, '''34962: GL_ARRAY_BUFFER (0x8892)
26214: what?
35040: GL_STREAM_DRAW (0x88E0)
3060: what?
''', args=['34962', '26214', '35040', str(0xbf4)])
@no_emterpreter
def test_biggerswitch(self):
num_cases = 20000
switch_case = run_process([PYTHON, path_from_root('tests', 'gen_large_switchcase.py'), str(num_cases)], stdout=PIPE, stderr=PIPE).stdout
self.do_run(switch_case, '''58996: 589965899658996
59297: 592975929759297
59598: default
59899: 598995989959899
Success!''')
@no_wasm_backend('no implementation of computed gotos')
def test_indirectbr(self):
Building.COMPILER_TEST_OPTS = [x for x in Building.COMPILER_TEST_OPTS if x != '-g']
self.do_run_in_out_file_test('tests', 'core', 'test_indirectbr')
@no_wasm_backend('no implementation of computed gotos')
def test_indirectbr_many(self):
self.do_run_in_out_file_test('tests', 'core', 'test_indirectbr_many')
def test_pack(self):
src = '''
#include <stdio.h>
#include <string.h>
#pragma pack(push,1)
typedef struct header
{
unsigned char id;
unsigned short colour;
unsigned char desc;
} header;
#pragma pack(pop)
typedef struct fatheader
{
unsigned char id;
unsigned short colour;
unsigned char desc;
} fatheader;
int main( int argc, const char *argv[] ) {
header h, *ph = 0;
fatheader fh, *pfh = 0;
printf("*%d,%d,%d*\\n", sizeof(header), (int)((int)&h.desc - (int)&h.id), (int)(&ph[1])-(int)(&ph[0]));
printf("*%d,%d,%d*\\n", sizeof(fatheader), (int)((int)&fh.desc - (int)&fh.id), (int)(&pfh[1])-(int)(&pfh[0]));
return 0;
}
'''
self.do_run(src, '*4,3,4*\n*6,4,6*')
def test_varargs(self):
self.do_run_in_out_file_test('tests', 'core', 'test_varargs')
@no_wasm_backend('Calling varargs across function calls is undefined behavior in C,'
' and asmjs and wasm implement it differently.')
def test_varargs_multi(self):
self.do_run_in_out_file_test('tests', 'core', 'test_varargs_multi')
@unittest.skip('clang cannot compile this code with that target yet')
def test_varargs_byval(self):
src = r'''
#include <stdio.h>
#include <stdarg.h>
typedef struct type_a {
union {
double f;
void *p;
int i;
short sym;
} value;
} type_a;
enum mrb_vtype {
MRB_TT_FALSE = 0, /* 0 */
MRB_TT_CLASS = 9 /* 9 */
};
typedef struct type_b {
enum mrb_vtype tt:8;
} type_b;
void print_type_a(int argc, ...);
void print_type_b(int argc, ...);
int main(int argc, char *argv[])
{
type_a a;
type_b b;
a.value.p = (void*) 0x12345678;
b.tt = MRB_TT_CLASS;
printf("The original address of a is: %p\n", a.value.p);
printf("The original type of b is: %d\n", b.tt);
print_type_a(1, a);
print_type_b(1, b);
return 0;
}
void print_type_a(int argc, ...) {
va_list ap;
type_a a;
va_start(ap, argc);
a = va_arg(ap, type_a);
va_end(ap);
printf("The current address of a is: %p\n", a.value.p);
}
void print_type_b(int argc, ...) {
va_list ap;
type_b b;
va_start(ap, argc);
b = va_arg(ap, type_b);
va_end(ap);
printf("The current type of b is: %d\n", b.tt);
}
'''
self.do_run(src, '''The original address of a is: 0x12345678
The original type of b is: 9
The current address of a is: 0x12345678
The current type of b is: 9
''')
def test_functionpointer_libfunc_varargs(self):
self.do_run_in_out_file_test('tests', 'core', 'test_functionpointer_libfunc_varargs')
def test_structbyval(self):
self.set_setting('INLINING_LIMIT', 50)
# part 1: make sure that normally, passing structs by value works
src = r'''
#include <stdio.h>
struct point
{
int x, y;
};
void dump(struct point p) {
p.x++; // should not modify
p.y++; // anything in the caller!
printf("dump: %d,%d\n", p.x, p.y);
}
void dumpmod(struct point *p) {
p->x++; // should not modify
p->y++; // anything in the caller!
printf("dump: %d,%d\n", p->x, p->y);
}
int main( int argc, const char *argv[] ) {
point p = { 54, 2 };
printf("pre: %d,%d\n", p.x, p.y);
dump(p);
void (*dp)(point p) = dump; // And, as a function pointer
dp(p);
printf("post: %d,%d\n", p.x, p.y);
dumpmod(&p);
dumpmod(&p);
printf("last: %d,%d\n", p.x, p.y);
return 0;
}
'''
self.do_run(src, 'pre: 54,2\ndump: 55,3\ndump: 55,3\npost: 54,2\ndump: 55,3\ndump: 56,4\nlast: 56,4')
# Check for lack of warning in the generated code (they should appear in part 2)
generated = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read()
assert 'Casting a function pointer type to another with a different number of arguments.' not in generated, 'Unexpected warning'
# part 2: make sure we warn about mixing c and c++ calling conventions here
if self.emcc_args != []:
# Optimized code is missing the warning comments
return
header = r'''
struct point
{
int x, y;
};
'''
open(os.path.join(self.get_dir(), 'header.h'), 'w').write(header)
supp = r'''
#include <stdio.h>
#include "header.h"
void dump(struct point p) {
p.x++; // should not modify
p.y++; // anything in the caller!
printf("dump: %d,%d\n", p.x, p.y);
}
'''
supp_name = os.path.join(self.get_dir(), 'supp.c')
open(supp_name, 'w').write(supp)
main = r'''
#include <stdio.h>
#include "header.h"
#ifdef __cplusplus
extern "C" {
#endif
void dump(struct point p);
#ifdef __cplusplus
}
#endif
int main( int argc, const char *argv[] ) {
struct point p = { 54, 2 };
printf("pre: %d,%d\n", p.x, p.y);
dump(p);
void (*dp)(struct point p) = dump; // And, as a function pointer
dp(p);
printf("post: %d,%d\n", p.x, p.y);
return 0;
}
'''
main_name = os.path.join(self.get_dir(), 'main.cpp')
open(main_name, 'w').write(main)
Building.emcc(supp_name)
Building.emcc(main_name)
all_name = os.path.join(self.get_dir(), 'all.bc')
Building.link([supp_name + '.o', main_name + '.o'], all_name)
# This will fail! See explanation near the warning we check for, in the compiler source code
run_process([PYTHON, EMCC, all_name] + self.emcc_args, check=False, stderr=PIPE)
# Check for warning in the generated code
generated = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read()
print('skipping C/C++ conventions warning check, since not i386-pc-linux-gnu', file=sys.stderr)
def test_stdlibs(self):
# safe heap prints a warning that messes up our output.
self.set_setting('SAFE_HEAP', 0)
# needs atexit
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_stdlibs')
def test_stdbool(self):
src = r'''
#include <stdio.h>
#include <stdbool.h>
int main() {
bool x = true;
bool y = false;
printf("*%d*\n", x != y);
return 0;
}
'''
self.do_run(src, '*1*', force_c=True)
def test_strtoll_hex(self):
# tests strtoll for hex strings (0x...)
self.do_run_in_out_file_test('tests', 'core', 'test_strtoll_hex')
def test_strtoll_dec(self):
# tests strtoll for decimal strings (0x...)
self.do_run_in_out_file_test('tests', 'core', 'test_strtoll_dec')
def test_strtoll_bin(self):
# tests strtoll for binary strings (0x...)
self.do_run_in_out_file_test('tests', 'core', 'test_strtoll_bin')
def test_strtoll_oct(self):
# tests strtoll for decimal strings (0x...)
self.do_run_in_out_file_test('tests', 'core', 'test_strtoll_oct')
def test_strtol_hex(self):
# tests strtoll for hex strings (0x...)
self.do_run_in_out_file_test('tests', 'core', 'test_strtol_hex')
def test_strtol_dec(self):
# tests strtoll for decimal strings (0x...)
self.do_run_in_out_file_test('tests', 'core', 'test_strtol_dec')
def test_strtol_bin(self):
# tests strtoll for binary strings (0x...)
self.do_run_in_out_file_test('tests', 'core', 'test_strtol_bin')
def test_strtol_oct(self):
# tests strtoll for decimal strings (0x...)
self.do_run_in_out_file_test('tests', 'core', 'test_strtol_oct')
def test_atexit(self):
# Confirms they are called in reverse order
# needs atexits
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_atexit')
def test_pthread_specific(self):
src = open(path_from_root('tests', 'pthread', 'specific.c'), 'r').read()
expected = open(path_from_root('tests', 'pthread', 'specific.c.txt'), 'r').read()
self.do_run(src, expected, force_c=True)
def test_pthread_equal(self):
self.do_run_in_out_file_test('tests', 'pthread', 'test_pthread_equal')
def test_tcgetattr(self):
src = open(path_from_root('tests', 'termios', 'test_tcgetattr.c'), 'r').read()
self.do_run(src, 'success', force_c=True)
def test_time(self):
src = open(path_from_root('tests', 'time', 'src.cpp'), 'r').read()
expected = open(path_from_root('tests', 'time', 'output.txt'), 'r').read()
self.do_run(src, expected)
if 'TZ' in os.environ:
print('TZ set in environment, skipping extra time zone checks')
else:
try:
for tz in ['EST+05EDT', 'UTC+0']:
print('extra tz test:', tz)
# Run the test with different time zone settings if
# possible. It seems that the TZ environment variable does not
# work all the time (at least it's not well respected by
# Node.js on Windows), but it does no harm either.
os.environ['TZ'] = tz
self.do_run(src, expected)
finally:
del os.environ['TZ']
def test_timeb(self):
# Confirms they are called in reverse order
self.do_run_in_out_file_test('tests', 'core', 'test_timeb')
def test_time_c(self):
self.do_run_in_out_file_test('tests', 'core', 'test_time_c')
def test_gmtime(self):
self.do_run_in_out_file_test('tests', 'core', 'test_gmtime')
def test_strptime_tm(self):
self.do_run_in_out_file_test('tests', 'core', 'test_strptime_tm')
def test_strptime_days(self):
self.do_run_in_out_file_test('tests', 'core', 'test_strptime_days')
def test_strptime_reentrant(self):
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_strptime_reentrant')
def test_strftime(self):
self.do_run_in_out_file_test('tests', 'core', 'test_strftime')
@no_wasm_backend("wasm backend doesn't compile intentional segfault into an abort() call. "
"It also doesn't segfault.")
def test_intentional_fault(self):
# Some programs intentionally segfault themselves, we should compile that into a throw
src = open(path_from_root('tests', 'core', 'test_intentional_fault.c'), 'r').read()
self.do_run(src, 'abort()' if self.run_name != 'asm2g' else 'abort("segmentation fault')
def test_trickystring(self):
self.do_run_in_out_file_test('tests', 'core', 'test_trickystring')
def test_statics(self):
self.do_run_in_out_file_test('tests', 'core', 'test_statics')
def test_copyop(self):
# clang generated code is vulnerable to this, as it uses
# memcpy for assignments, with hardcoded numbers of bytes
# (llvm-gcc copies items one by one). See QUANTUM_SIZE in
# settings.js.
test_path = path_from_root('tests', 'core', 'test_copyop')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output)
def test_memcpy_memcmp(self):
self.banned_js_engines = [V8_ENGINE] # Currently broken under V8_ENGINE but not node
test_path = path_from_root('tests', 'core', 'test_memcpy_memcmp')
src, output = (test_path + s for s in ('.c', '.out'))
def check(result, err):
result = result.replace('\n \n', '\n') # remove extra node output
return hashlib.sha1(result.encode('utf-8')).hexdigest()
self.do_run_from_file(src, output, output_nicerizer=check)
def test_memcpy2(self):
self.do_run_in_out_file_test('tests', 'core', 'test_memcpy2')
def test_memcpy3(self):
self.do_run_in_out_file_test('tests', 'core', 'test_memcpy3')
def test_memcpy_alignment(self):
self.do_run(open(path_from_root('tests', 'test_memcpy_alignment.cpp'), 'r').read(), 'OK.')
def test_memset_alignment(self):
self.do_run(open(path_from_root('tests', 'test_memset_alignment.cpp'), 'r').read(), 'OK.')
def test_memset(self):
self.do_run_in_out_file_test('tests', 'core', 'test_memset')
def test_getopt(self):
test_path = path_from_root('tests', 'core', 'test_getopt')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output, args=['-t', '12', '-n', 'foobar'])
def test_getopt_long(self):
test_path = path_from_root('tests', 'core', 'test_getopt_long')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output, args=['--file', 'foobar', '-b'])
def test_memmove(self):
self.do_run_in_out_file_test('tests', 'core', 'test_memmove')
def test_memmove2(self):
self.do_run_in_out_file_test('tests', 'core', 'test_memmove2')
def test_memmove3(self):
self.do_run_in_out_file_test('tests', 'core', 'test_memmove3')
def test_flexarray_struct(self):
self.do_run_in_out_file_test('tests', 'core', 'test_flexarray_struct')
def test_bsearch(self):
self.do_run_in_out_file_test('tests', 'core', 'test_bsearch')
@no_wasm_backend("wasm backend has no support for fastcomp's -emscripten-assertions flag")
def test_stack_overflow(self):
self.set_setting('ASSERTIONS', 1)
self.do_run(open(path_from_root('tests', 'core', 'stack_overflow.cpp')).read(), 'Stack overflow!')
def test_stackAlloc(self):
self.do_run_in_out_file_test('tests', 'core', 'stackAlloc')
def test_nestedstructs(self):
src = '''
#include <stdio.h>
#include "emscripten.h"
struct base {
int x;
float y;
union {
int a;
float b;
};
char c;
};
struct hashtableentry {
int key;
base data;
};
struct hashset {
typedef hashtableentry entry;
struct chain { entry elem; chain *next; };
// struct chainchunk { chain chains[100]; chainchunk *next; };
};
struct hashtable : hashset {
hashtable() {
base *b = NULL;
entry *e = NULL;
chain *c = NULL;
printf("*%d,%d,%d,%d,%d,%d|%d,%d,%d,%d,%d,%d,%d,%d|%d,%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n",
sizeof(base),
int(&(b->x)), int(&(b->y)), int(&(b->a)), int(&(b->b)), int(&(b->c)),
sizeof(hashtableentry),
int(&(e->key)), int(&(e->data)), int(&(e->data.x)), int(&(e->data.y)), int(&(e->data.a)), int(&(e->data.b)), int(&(e->data.c)),
sizeof(hashset::chain),
int(&(c->elem)), int(&(c->next)), int(&(c->elem.key)), int(&(c->elem.data)), int(&(c->elem.data.x)), int(&(c->elem.data.y)), int(&(c->elem.data.a)), int(&(c->elem.data.b)), int(&(c->elem.data.c))
);
}
};
struct B { char buffer[62]; int last; char laster; char laster2; };
struct Bits {
unsigned short A : 1;
unsigned short B : 1;
unsigned short C : 1;
unsigned short D : 1;
unsigned short x1 : 1;
unsigned short x2 : 1;
unsigned short x3 : 1;
unsigned short x4 : 1;
};
int main() {
hashtable t;
// Part 2 - the char[] should be compressed, BUT have a padding space at the end so the next
// one is aligned properly. Also handle char; char; etc. properly.
B *b = NULL;
printf("*%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n", int(b), int(&(b->buffer)), int(&(b->buffer[0])), int(&(b->buffer[1])), int(&(b->buffer[2])),
int(&(b->last)), int(&(b->laster)), int(&(b->laster2)), sizeof(B));
// Part 3 - bitfields, and small structures
Bits *b2 = NULL;
printf("*%d*\\n", sizeof(Bits));
return 0;
}
'''
# Bloated memory; same layout as C/C++
self.do_run(src, '*16,0,4,8,8,12|20,0,4,4,8,12,12,16|24,0,20,0,4,4,8,12,12,16*\n*0,0,0,1,2,64,68,69,72*\n*2*')
@unittest.skip('BUILD_AS_SHARED_LIB=2 is deprecated')
def test_runtimelink(self):
if Building.LLVM_OPTS:
self.skipTest('LLVM opts will optimize printf into puts in the parent, and the child will still look for puts')
if self.get_setting('ASM_JS'):
self.skipTest('asm does not support runtime linking')
main, supp = self.setup_runtimelink_test()
self.banned_js_engines = [NODE_JS] # node's global scope behaves differently than everything else, needs investigation FIXME
self.set_setting('LINKABLE', 1)
self.set_setting('BUILD_AS_SHARED_LIB', 2)
self.build(supp, self.get_dir(), self.in_dir('supp.cpp'))
shutil.move(self.in_dir('supp.cpp.o.js'), self.in_dir('liblib.so'))
self.set_setting('BUILD_AS_SHARED_LIB', 0)
self.set_setting('RUNTIME_LINKED_LIBS', ['liblib.so'])
self.do_run(main, 'supp: 54,2\nmain: 56\nsupp see: 543\nmain see: 76\nok.')
def prep_dlfcn_lib(self):
self.set_setting('MAIN_MODULE', 0)
self.set_setting('SIDE_MODULE', 1)
def prep_dlfcn_main(self):
self.set_setting('MAIN_MODULE', 1)
self.set_setting('SIDE_MODULE', 0)
with open('lib_so_pre.js', 'w') as f:
f.write('''
if (!Module['preRun']) Module['preRun'] = [];
Module['preRun'].push(function() { FS.createDataFile('/', 'liblib.so', %s, true, false, false); });
''' % str(list(bytearray(open('liblib.so', 'rb').read()))))
self.emcc_args += ['--pre-js', 'lib_so_pre.js']
def build_dlfcn_lib(self, lib_src, dirname, filename):
if self.get_setting('WASM'):
# emcc emits a wasm in this case
self.build(lib_src, dirname, filename, js_outfile=False)
shutil.move(filename + '.o.wasm', os.path.join(dirname, 'liblib.so'))
else:
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
@needs_dlfcn
def test_dlfcn_missing(self):
self.set_setting('MAIN_MODULE', 1)
src = r'''
#include <dlfcn.h>
#include <stdio.h>
#include <assert.h>
int main() {
void* lib_handle = dlopen("libfoo.so", RTLD_NOW);
assert(!lib_handle);
printf("error: %s\n", dlerror());
return 0;
}
'''
self.do_run(src, 'error: Could not find dynamic lib: libfoo.so\n')
@needs_dlfcn
def test_dlfcn_basic(self):
self.prep_dlfcn_lib()
lib_src = '''
#include <cstdio>
class Foo {
public:
Foo() {
puts("Constructing lib object.");
}
};
Foo global;
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
self.build_dlfcn_lib(lib_src, dirname, filename)
self.prep_dlfcn_main()
src = '''
#include <cstdio>
#include <dlfcn.h>
class Bar {
public:
Bar() {
puts("Constructing main object.");
}
};
Bar global;
int main() {
dlopen("liblib.so", RTLD_NOW);
return 0;
}
'''
self.do_run(src, 'Constructing main object.\nConstructing lib object.\n')
@needs_dlfcn
def test_dlfcn_i64(self):
# avoid using asm2wasm imports, which don't work in side modules yet (should they?)
self.set_setting('BINARYEN_TRAP_MODE', 'clamp')
self.prep_dlfcn_lib()
self.set_setting('EXPORTED_FUNCTIONS', ['_foo'])
lib_src = '''
int foo(int x) {
return (long long)x / (long long)1234;
}
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.c')
self.build_dlfcn_lib(lib_src, dirname, filename)
self.prep_dlfcn_main()
self.set_setting('EXPORTED_FUNCTIONS', ['_main'])
src = r'''
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
typedef int (*intfunc)(int);
void *p;
int main() {
p = malloc(1024);
void *lib_handle = dlopen("liblib.so", 0);
if (!lib_handle) {
puts(dlerror());
abort();
}
printf("load %p\n", lib_handle);
intfunc x = (intfunc)dlsym(lib_handle, "foo");
printf("foo func %p\n", x);
if (p == 0) return 1;
printf("|%d|\n", x(81234567));
return 0;
}
'''
self.do_run(src, '|65830|')
@no_wasm # TODO: EM_ASM in shared wasm modules, stored inside the wasm somehow
@needs_dlfcn
def test_dlfcn_em_asm(self):
self.prep_dlfcn_lib()
lib_src = '''
#include <emscripten.h>
class Foo {
public:
Foo() {
EM_ASM( out("Constructing lib object.") );
}
};
Foo global;
'''
filename = 'liblib.cpp'
self.build_dlfcn_lib(lib_src, self.get_dir(), filename)
self.prep_dlfcn_main()
src = '''
#include <emscripten.h>
#include <dlfcn.h>
class Bar {
public:
Bar() {
EM_ASM( out("Constructing main object.") );
}
};
Bar global;
int main() {
dlopen("liblib.so", RTLD_NOW);
EM_ASM( out("All done.") );
return 0;
}
'''
self.do_run(src, 'Constructing main object.\nConstructing lib object.\nAll done.\n')
@needs_dlfcn
def test_dlfcn_qsort(self):
self.prep_dlfcn_lib()
self.set_setting('EXPORTED_FUNCTIONS', ['_get_cmp'])
lib_src = '''
int lib_cmp(const void* left, const void* right) {
const int* a = (const int*) left;
const int* b = (const int*) right;
if(*a > *b) return 1;
else if(*a == *b) return 0;
else return -1;
}
typedef int (*CMP_TYPE)(const void*, const void*);
extern "C" CMP_TYPE get_cmp() {
return lib_cmp;
}
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
self.build_dlfcn_lib(lib_src, dirname, filename)
self.prep_dlfcn_main()
self.set_setting('EXPORTED_FUNCTIONS', ['_main', '_malloc'])
src = '''
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
typedef int (*CMP_TYPE)(const void*, const void*);
int main_cmp(const void* left, const void* right) {
const int* a = (const int*) left;
const int* b = (const int*) right;
if(*a < *b) return 1;
else if(*a == *b) return 0;
else return -1;
}
int main() {
void* lib_handle;
CMP_TYPE (*getter_ptr)();
CMP_TYPE lib_cmp_ptr;
int arr[5] = {4, 2, 5, 1, 3};
qsort((void*)arr, 5, sizeof(int), main_cmp);
printf("Sort with main comparison: ");
for (int i = 0; i < 5; i++) {
printf("