summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rwxr-xr-xemcc21
-rwxr-xr-xemscripten.py114
-rw-r--r--package.json7
-rw-r--r--src/jsifier.js30
-rw-r--r--src/library.js1586
-rw-r--r--src/library_browser.js22
-rw-r--r--src/library_fs.js12
-rw-r--r--src/library_sdl.js32
-rw-r--r--src/library_sockfs.js568
-rw-r--r--src/library_tty.js43
-rw-r--r--src/modules.js1
-rw-r--r--src/settings.js46
-rw-r--r--src/shell_sharedlib.js19
-rw-r--r--system/include/netdb.h1
-rw-r--r--tests/browser_main.cpp42
-rw-r--r--tests/browser_module.cpp15
-rw-r--r--tests/dlmalloc_proxy.c85
-rw-r--r--tests/emscripten_get_now.cpp46
-rw-r--r--tests/module/test_stdin.c57
-rw-r--r--tests/msvc10/emscripten_api_browser.vcxproj12
-rw-r--r--tests/msvc10/glbook_10_MultiTexture.vcxproj2
-rw-r--r--tests/msvc10/glbook_11_Multisample.vcxproj2
-rw-r--r--tests/msvc10/glbook_11_Stencil_Test.vcxproj2
-rw-r--r--tests/msvc10/glbook_13_ParticleSystem.vcxproj2
-rw-r--r--tests/msvc10/glbook_15_Hello_Triangle_KD.vcxproj2
-rw-r--r--tests/msvc10/glbook_2_Hello_Triangle.vcxproj2
-rw-r--r--tests/msvc10/glbook_8_Simple_VertexShader.vcxproj2
-rw-r--r--tests/msvc10/glbook_9_MipMap2D.vcxproj2
-rw-r--r--tests/msvc10/glbook_9_Simple_Texture2D.vcxproj2
-rw-r--r--tests/msvc10/glbook_9_Simple_TextureCubemap.vcxproj2
-rw-r--r--tests/msvc10/glbook_9_TextureWrap.vcxproj2
-rw-r--r--tests/msvc10/new.vcxproj89
-rw-r--r--tests/msvc10/sdl_audio.vcxproj16
-rw-r--r--tests/msvc10/sdl_canvas.vcxproj10
-rw-r--r--tests/msvc10/sdl_gl_read.vcxproj12
-rw-r--r--tests/msvc10/sdl_image.vcxproj17
-rw-r--r--tests/msvc10/sdl_key.vcxproj10
-rw-r--r--tests/msvc10/sdl_mouse.vcxproj10
-rw-r--r--tests/msvc10/sdl_ogl.vcxproj16
-rw-r--r--tests/msvc10/tests_msvc10.sln7
-rw-r--r--tests/msvc10/twopart.vcxproj10
-rwxr-xr-xtests/runner.py4
-rw-r--r--tests/sockets/test_getaddrinfo.c197
-rw-r--r--tests/sockets/test_gethostbyname.c (renamed from tests/sockets/test_sockets_gethostbyname.c)14
-rw-r--r--tests/sockets/test_getnameinfo.c101
-rw-r--r--tests/sockets/test_sockets_echo_client.c19
-rw-r--r--tests/sockets/test_sockets_echo_server.c35
-rw-r--r--tests/sockets/test_sockets_msg.h4
-rw-r--r--tests/sockets/test_sockets_partial_server.c13
-rw-r--r--tests/sockets/test_sockets_select_server_closes_connection_client_rw.c14
-rw-r--r--tests/sockets/test_sockets_select_server_down_client.c2
-rw-r--r--tests/termios/test_tcgetattr.c61
-rw-r--r--tests/test_browser.py8
-rw-r--r--tests/test_core.py579
-rw-r--r--tests/test_other.py31
-rw-r--r--tests/test_sockets.py263
-rw-r--r--tools/jsrun.py4
-rw-r--r--tools/shared.py61
59 files changed, 3285 insertions, 1105 deletions
diff --git a/.gitignore b/.gitignore
index 92043241..747394e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,8 @@
*.bc
src/relooper*.js
+node_modules/
+
# Ignore generated files
src/relooper.js
src/relooper.js.raw.js
diff --git a/emcc b/emcc
index a07ce4f1..93621a83 100755
--- a/emcc
+++ b/emcc
@@ -663,6 +663,12 @@ if '-M' in sys.argv or '-MM' in sys.argv:
logging.debug('just dependencies: ' + ' '.join(cmd))
exit(subprocess.call(cmd))
+if '-E' in sys.argv:
+ # Just run the preprocessor
+ cmd = [CC] + sys.argv[1:]
+ logging.debug('just preprocssor ' + ' '.join(cmd))
+ exit(subprocess.call(cmd))
+
# Check if a target is specified
target = None
for i in range(len(sys.argv)-1):
@@ -1070,9 +1076,6 @@ try:
shared.Settings.CORRECT_OVERFLOWS = 1
assert not shared.Settings.PGO, 'cannot run PGO in ASM_JS mode'
- if shared.Settings.ASSERTIONS and shared.Settings.ALIASING_FUNCTION_POINTERS:
- logging.warning('ALIASING_FUNCTION_POINTERS is on, function pointer comparisons may be invalid across types')
-
if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2:
debug_level = 4 # must keep debug info to do line-by-line operations
@@ -1107,13 +1110,21 @@ try:
shared.Settings.LINKABLE = 1 # TODO: add FORCE_DCE option for the brave people that do want to dce here and in side modules
debug_level = max(debug_level, 2)
- if shared.Settings.DLOPEN_SUPPORT:
- shared.Settings.LINKABLE = 1
+ if shared.Settings.ASSERTIONS and shared.Settings.ALIASING_FUNCTION_POINTERS:
+ logging.warning('ALIASING_FUNCTION_POINTERS is on, function pointer comparisons may be invalid across types')
if shared.Settings.STB_IMAGE and final_suffix in JS_CONTAINING_SUFFIXES:
input_files.append(shared.path_from_root('third_party', 'stb_image.c'))
shared.Settings.EXPORTED_FUNCTIONS += ['_stbi_load', '_stbi_load_from_memory', '_stbi_image_free']
+ if type(shared.Settings.EXPORTED_FUNCTIONS) in (list, tuple):
+ # always need malloc and free to be kept alive and exported, for internal use and other modules
+ for required_export in ['_malloc', '_free']:
+ if required_export not in shared.Settings.EXPORTED_FUNCTIONS:
+ shared.Settings.EXPORTED_FUNCTIONS.append(required_export)
+ else:
+ logging.debug('using response file for EXPORTED_FUNCTIONS, make sure it includes _malloc and _free')
+
## Compile source code to bitcode
logging.debug('compiling to bitcode')
diff --git a/emscripten.py b/emscripten.py
index c5e235d8..257527fe 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -444,6 +444,11 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
forwarded_json['Functions']['libraryFunctions'].get('llvm_ctlz_i32'):
basic_vars += ['cttz_i8', 'ctlz_i8']
+ if settings.get('DLOPEN_SUPPORT'):
+ for sig in last_forwarded_json['Functions']['tables'].iterkeys():
+ basic_vars.append('F_BASE_%s' % sig)
+ asm_setup += ' var F_BASE_%s = %s;\n' % (sig, 'FUNCTION_TABLE_OFFSET' if settings.get('SIDE_MODULE') else '0') + '\n'
+
asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew'] + ['setTempRet%d' % i for i in range(10)]
# function tables
def asm_coerce(value, sig):
@@ -475,8 +480,12 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
''' % (sig, i, args, arg_coercions, jsret))
from tools import shared
+ shared.Settings.copy(settings)
asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n'
basic_funcs.append('invoke_%s' % sig)
+ if settings.get('DLOPEN_SUPPORT'):
+ asm_setup += '\n' + shared.JS.make_extcall(sig) + '\n'
+ basic_funcs.append('extCall_%s' % sig)
# calculate exports
exported_implemented_functions = list(exported_implemented_functions)
@@ -606,23 +615,36 @@ function setTempRet%d(value) {
// EMSCRIPTEN_END_ASM
(%s, %s, buffer);
%s;
+''' % (pre_tables + '\n'.join(function_tables_impls) + '\n' + function_tables_defs.replace('\n', '\n '), exports, the_global, sending, receiving)]
+
+ if not settings.get('SIDE_MODULE'):
+ funcs_js.append('''
Runtime.stackAlloc = function(size) { return asm['stackAlloc'](size) };
Runtime.stackSave = function() { return asm['stackSave']() };
Runtime.stackRestore = function(top) { asm['stackRestore'](top) };
-''' % (pre_tables + '\n'.join(function_tables_impls) + '\n' + function_tables_defs.replace('\n', '\n '), exports, the_global, sending, receiving)]
+''')
# Set function table masks
- def function_table_maskize(js):
- masks = {}
- default = None
- for sig, table in last_forwarded_json['Functions']['tables'].iteritems():
- masks[sig] = str(table.count(','))
- default = sig
+ masks = {}
+ max_mask = 0
+ for sig, table in last_forwarded_json['Functions']['tables'].iteritems():
+ mask = table.count(',')
+ masks[sig] = str(mask)
+ max_mask = max(mask, max_mask)
+ def function_table_maskize(js, masks):
def fix(m):
sig = m.groups(0)[0]
return masks[sig]
return re.sub(r'{{{ FTM_([\w\d_$]+) }}}', lambda m: fix(m), js) # masks[m.groups(0)[0]]
- funcs_js = map(function_table_maskize, funcs_js)
+ funcs_js = map(lambda js: function_table_maskize(js, masks), funcs_js)
+
+ if settings.get('DLOPEN_SUPPORT'):
+ funcs_js.append('''
+ asm.maxFunctionIndex = %(max_mask)d;
+ DLFCN.registerFunctions(asm, %(max_mask)d+1, %(sigs)s, Module);
+ Module.SYMBOL_TABLE = SYMBOL_TABLE;
+''' % { 'max_mask': max_mask, 'sigs': str(map(str, last_forwarded_json['Functions']['tables'].keys())) })
+
else:
function_tables_defs = '\n'.join([table for table in last_forwarded_json['Functions']['tables'].itervalues()])
outfile.write(function_tables_defs)
@@ -637,13 +659,18 @@ Runtime.stackRestore = function(top) { asm['stackRestore'](top) };
symbol_table = {}
for k, v in forwarded_json['Variables']['indexedGlobals'].iteritems():
if forwarded_json['Variables']['globals'][k]['named']:
- symbol_table[k] = v + forwarded_json['Runtime']['GLOBAL_BASE']
+ symbol_table[k] = str(v + forwarded_json['Runtime']['GLOBAL_BASE'])
for raw in last_forwarded_json['Functions']['tables'].itervalues():
if raw == '': continue
table = map(string.strip, raw[raw.find('[')+1:raw.find(']')].split(","))
- symbol_table.update(map(lambda x: (x[1], x[0]),
- filter(lambda x: x[1] != '0', enumerate(table))))
- outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table))
+ for i in range(len(table)):
+ value = table[i]
+ if value != '0':
+ if settings.get('SIDE_MODULE'):
+ symbol_table[value] = 'FUNCTION_TABLE_OFFSET+' + str(i)
+ else:
+ symbol_table[value] = str(i)
+ outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table).replace('"', ''))
for funcs_js_item in funcs_js: # do this loop carefully to save memory
funcs_js_item = indexize(funcs_js_item)
@@ -663,69 +690,6 @@ def main(args, compiler_engine, cache, jcache, relooper, temp_files, DEBUG, DEBU
name, value = setting.strip().split('=', 1)
settings[name] = json.loads(value)
- # Add header defines to settings
- defines = {}
- include_root = path_from_root('system', 'include')
- headers = args.headers[0].split(',') if len(args.headers) > 0 else []
- seen_headers = set()
- while len(headers) > 0:
- header = headers.pop(0)
- if not os.path.isabs(header):
- header = os.path.join(include_root, header)
- seen_headers.add(header)
- for line in open(header, 'r'):
- line = line.replace('\t', ' ')
- m = re.match('^ *# *define +(?P<name>[-\w_.]+) +\(?(?P<value>[-\w_.|]+)\)?.*', line)
- if not m:
- # Catch enum defines of a very limited sort
- m = re.match('^ +(?P<name>[A-Z_\d]+) += +(?P<value>\d+).*', line)
- if m:
- if m.group('name') != m.group('value'):
- defines[m.group('name')] = m.group('value')
- #else:
- # print 'Warning: %s #defined to itself' % m.group('name') # XXX this can happen if we are set to be equal to an enum (with the same name)
- m = re.match('^ *# *include *["<](?P<name>[\w_.-/]+)[">].*', line)
- if m:
- # Find this file
- found = False
- for w in [w for w in os.walk(include_root)]:
- for f in w[2]:
- curr = os.path.join(w[0], f)
- if curr.endswith(m.group('name')) and curr not in seen_headers:
- headers.append(curr)
- found = True
- break
- if found: break
- #assert found, 'Could not find header: ' + m.group('name')
- if len(defines) > 0:
- def lookup(value):
- try:
- while not unicode(value).isnumeric():
- value = defines[value]
- return value
- except:
- pass
- try: # 0x300 etc.
- value = eval(value)
- return value
- except:
- pass
- try: # CONST1|CONST2
- parts = map(lookup, value.split('|'))
- value = reduce(lambda a, b: a|b, map(eval, parts))
- return value
- except:
- pass
- return None
- for key, value in defines.items():
- value = lookup(value)
- if value is not None:
- defines[key] = str(value)
- else:
- del defines[key]
- #print >> sys.stderr, 'new defs:', str(defines).replace(',', ',\n '), '\n\n'
- settings.setdefault('C_DEFINES', {}).update(defines)
-
# libraries
libraries = args.libraries[0].split(',') if len(args.libraries) > 0 else []
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..a1447c9f
--- /dev/null
+++ b/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "emscripten",
+ "version": "1.0.0",
+ "dependencies": {
+ "ws": "~0.4.28"
+ }
+}
diff --git a/src/jsifier.js b/src/jsifier.js
index 7273f54c..8592364d 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1405,7 +1405,10 @@ function JSify(data, functionsOnly, givenFunctions) {
// We cannot compile assembly. See comment in intertyper.js:'Call'
assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!');
+ var extCall = false;
+
if (ASM_JS && funcData.setjmpTable) forceByPointer = true; // in asm.js mode, we must do an invoke for each call
+ if (ASM_JS && DLOPEN_SUPPORT && !invoke) extCall = true; // go out, to be able to access other modules TODO: optimize
ident = Variables.resolveAliasToIdent(ident);
var shortident = ident.slice(1);
@@ -1466,7 +1469,7 @@ function JSify(data, functionsOnly, givenFunctions) {
args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) });
if (ASM_JS) {
- if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || funcData.setjmpTable) {
+ if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || extCall || funcData.setjmpTable) {
args = args.map(function(arg, i) { return asmCoercion(arg, argsTypes[i]) });
} else {
args = args.map(function(arg, i) { return asmEnsureFloat(arg, argsTypes[i]) });
@@ -1556,17 +1559,17 @@ function JSify(data, functionsOnly, givenFunctions) {
var sig = Functions.getSignature(returnType, argsTypes, hasVarArgs);
if (ASM_JS) {
assert(returnType.search(/\("'\[,/) == -1); // XXX need isFunctionType(type, out)
- var functionTableCall = !byPointerForced && !funcData.setjmpTable && !invoke;
+ Functions.neededTables[sig] = 1;
+ var functionTableCall = !byPointerForced && !funcData.setjmpTable && !invoke && !extCall;
if (functionTableCall) {
// normal asm function pointer call
callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py
- Functions.neededTables[sig] = 1;
} else {
- // This is a call through an invoke_*, either a forced one, or a setjmp-required one
+ // This is a call through an invoke_* or extCall, either a forced one, or a setjmp-required one
// note: no need to update argsTypes at this point
if (byPointerForced) Functions.unimplementedFunctions[callIdent] = sig;
args.unshift(byPointerForced ? Functions.getIndex(callIdent, sig) : asmCoercion(callIdent, 'i32'));
- callIdent = 'invoke_' + sig;
+ callIdent = (extCall ? 'extCall' : 'invoke') + '_' + sig;
}
} else if (SAFE_DYNCALLS) {
assert(!ASM_JS, 'cannot emit safe dyncalls in asm');
@@ -1660,8 +1663,13 @@ function JSify(data, functionsOnly, givenFunctions) {
Variables.generatedGlobalBase = true;
// Globals are done, here is the rest of static memory
assert((TARGET_LE32 && Runtime.GLOBAL_BASE == 8) || (TARGET_X86 && Runtime.GLOBAL_BASE == 4)); // this is assumed in e.g. relocations for linkable modules
- print('STATIC_BASE = ' + Runtime.GLOBAL_BASE + ';\n');
- print('STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n');
+ if (!SIDE_MODULE) {
+ print('STATIC_BASE = ' + Runtime.GLOBAL_BASE + ';\n');
+ print('STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n');
+ } else {
+ print('H_BASE = parentModule["_malloc"](' + Runtime.alignMemory(Variables.nextIndexedOffset) + ' + Runtime.GLOBAL_BASE);\n');
+ print('// STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n'); // comment as metadata only
+ }
}
var generated = itemsDict.function.concat(itemsDict.type).concat(itemsDict.GlobalVariableStub).concat(itemsDict.GlobalVariable);
print(generated.map(function(item) { return item.JS; }).join('\n'));
@@ -1688,7 +1696,7 @@ function JSify(data, functionsOnly, givenFunctions) {
return true;
});
// write out the singleton big memory initialization value
- print('/* memory initializer */ ' + makePointer(memoryInitialization, null, 'ALLOC_NONE', 'i8', 'Runtime.GLOBAL_BASE', true));
+ print('/* memory initializer */ ' + makePointer(memoryInitialization, null, 'ALLOC_NONE', 'i8', 'Runtime.GLOBAL_BASE' + (SIDE_MODULE ? '+H_BASE' : ''), true));
} else {
print('/* no memory initializer */'); // test purposes
}
@@ -1700,7 +1708,7 @@ function JSify(data, functionsOnly, givenFunctions) {
print('}\n');
if (USE_TYPED_ARRAYS == 2) {
- if (!BUILD_AS_SHARED_LIB) {
+ if (!BUILD_AS_SHARED_LIB && !SIDE_MODULE) {
print('var tempDoublePtr = Runtime.alignMemory(allocate(12, "i8", ALLOC_STATIC), 8);\n');
print('assert(tempDoublePtr % 8 == 0);\n');
print('function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much\n');
@@ -1754,7 +1762,7 @@ function JSify(data, functionsOnly, givenFunctions) {
legalizedI64s = legalizedI64sDefault;
- if (!BUILD_AS_SHARED_LIB) {
+ if (!BUILD_AS_SHARED_LIB && !SIDE_MODULE) {
print('STACK_BASE = STACKTOP = Runtime.alignMemory(STATICTOP);\n');
print('staticSealed = true; // seal the static portion of memory\n');
print('STACK_MAX = STACK_BASE + ' + TOTAL_STACK + ';\n');
@@ -1779,7 +1787,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}
}
- if (abortExecution) throw 'Aborting compilation due to previous warnings';
+ if (abortExecution) throw 'Aborting compilation due to previous errors';
if (phase == 'pre' || phase == 'funcs') {
PassManager.serialize();
diff --git a/src/library.js b/src/library.js
index 3ba2f56b..7894e033 100644
--- a/src/library.js
+++ b/src/library.js
@@ -600,31 +600,32 @@ LibraryManager.library = {
// poll.h
// ==========================================================================
+ __DEFAULT_POLLMASK: {{{ cDefine('POLLIN') }}} | {{{ cDefine('POLLOUT') }}},
__pollfd_struct_layout: Runtime.generateStructInfo([
['i32', 'fd'],
['i16', 'events'],
['i16', 'revents']]),
- poll__deps: ['$FS', '__pollfd_struct_layout'],
+ poll__deps: ['$FS', '__DEFAULT_POLLMASK', '__pollfd_struct_layout'],
poll: function(fds, nfds, timeout) {
// int poll(struct pollfd fds[], nfds_t nfds, int timeout);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html
- // NOTE: This is pretty much a no-op mimicking glibc.
var offsets = ___pollfd_struct_layout;
var nonzero = 0;
for (var i = 0; i < nfds; i++) {
var pollfd = fds + ___pollfd_struct_layout.__size__ * i;
var fd = {{{ makeGetValue('pollfd', 'offsets.fd', 'i32') }}};
var events = {{{ makeGetValue('pollfd', 'offsets.events', 'i16') }}};
- var revents = 0;
+ var mask = {{{ cDefine('POLLNVAL') }}};
var stream = FS.getStream(fd);
if (stream) {
- if (events & {{{ cDefine('POLLIN') }}}) revents |= {{{ cDefine('POLLIN') }}};
- if (events & {{{ cDefine('POLLOUT') }}}) revents |= {{{ cDefine('POLLOUT') }}};
- } else {
- if (events & {{{ cDefine('POLLNVAL') }}}) revents |= {{{ cDefine('POLLNVAL') }}};
+ mask = ___DEFAULT_POLLMASK;
+ if (stream.stream_ops.poll) {
+ mask = stream.stream_ops.poll(stream);
+ }
}
- if (revents) nonzero++;
- {{{ makeSetValue('pollfd', 'offsets.revents', 'revents', 'i16') }}}
+ mask &= events | {{{ cDefine('POLLERR') }}} | {{{ cDefine('POLLHUP') }}};
+ if (mask) nonzero++;
+ {{{ makeSetValue('pollfd', 'offsets.revents', 'mask', 'i16') }}}
}
return nonzero;
},
@@ -723,7 +724,7 @@ LibraryManager.library = {
FS.close(stream);
return 0;
} catch (e) {
- FS.handleFSError(e);;
+ FS.handleFSError(e);
return -1;
}
},
@@ -1006,9 +1007,11 @@ LibraryManager.library = {
return -1;
}
+#if SOCKET_WEBRTC
if (stream && ('socket' in stream)) {
return _recv(fildes, buf, nbyte, 0);
}
+#endif
try {
var slab = {{{ makeGetSlabs('buf', 'i8', true) }}};
@@ -1139,9 +1142,11 @@ LibraryManager.library = {
return -1;
}
+#if SOCKET_WEBRTC
if (stream && ('socket' in stream)) {
return _send(fildes, buf, nbyte, 0);
}
+#endif
try {
var slab = {{{ makeGetSlabs('buf', 'i8', true) }}};
@@ -2261,7 +2266,11 @@ LibraryManager.library = {
// void clearerr(FILE *stream);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/clearerr.html
stream = FS.getStream(stream);
- if (stream) stream.error = false;
+ if (!stream) {
+ return;
+ }
+ stream.eof = false;
+ stream.error = false;
},
fclose__deps: ['close', 'fsync'],
fclose: function(stream) {
@@ -2322,7 +2331,6 @@ LibraryManager.library = {
if (streamObj.eof || streamObj.error) return -1;
var ret = _fread(_fgetc.ret, 1, 1, stream);
if (ret == 0) {
- streamObj.eof = true;
return -1;
} else if (ret == -1) {
streamObj.error = true;
@@ -4724,16 +4732,22 @@ LibraryManager.library = {
erff: 'erf',
log: 'Math.log',
logf: 'Math.log',
+ logl: 'Math.log',
sqrt: 'Math.sqrt',
sqrtf: 'Math.sqrt',
+ sqrtl: 'Math.sqrt',
fabs: 'Math.abs',
fabsf: 'Math.abs',
+ fabsl: 'Math.abs',
ceil: 'Math.ceil',
ceilf: 'Math.ceil',
+ ceill: 'Math.ceil',
floor: 'Math.floor',
floorf: 'Math.floor',
+ floorl: 'Math.floor',
pow: 'Math.pow',
powf: 'Math.pow',
+ powl: 'Math.pow',
llvm_sqrt_f32: 'Math.sqrt',
llvm_sqrt_f64: 'Math.sqrt',
llvm_pow_f32: 'Math.pow',
@@ -5004,24 +5018,74 @@ LibraryManager.library = {
// being compiled. Not sure how to tell LLVM to not do so.
// ==========================================================================
- // Data for dlfcn.h.
- $DLFCN_DATA: {
+ $DLFCN: {
+#if DLOPEN_SUPPORT
+ // extra asm.js dlopen support
+ functionTable: [], // will contain objects mapping sigs to js functions that call into the right asm module with the right index
+
+ registerFunctions: function(asm, num, sigs, jsModule) {
+ // use asm module dynCall_* from functionTable
+ if (num % 2 == 1) num++; // keep pointers even
+ var table = DLFCN.functionTable;
+ var from = table.length;
+ assert(from % 2 == 0);
+ for (var i = 0; i < num; i++) {
+ table[from + i] = {};
+ sigs.forEach(function(sig) { // TODO: new Function etc.
+ var full = 'dynCall_' + sig;
+ table[from + i][sig] = function() {
+ arguments[0] -= from;
+ return asm[full].apply(null, arguments);
+ }
+ });
+ }
+
+ if (jsModule.cleanups) {
+ var newLength = table.length;
+ jsModule.cleanups.push(function() {
+ if (table.length === newLength) {
+ table.length = from; // nothing added since, just shrink
+ } else {
+ // something was added above us, clear and leak the span
+ for (var i = 0; i < num; i++) {
+ table[from + i] = null;
+ }
+ }
+ while (table.length > 0 && table[table.length-1] === null) table.pop();
+ });
+ }
+
+ // patch js module dynCall_* to use functionTable
+ sigs.forEach(function(sig) {
+ jsModule['dynCall_' + sig] = function() {
+ return table[arguments[0]][sig].apply(null, arguments);
+ };
+ });
+ },
+#endif
+
error: null,
errorMsg: null,
loadedLibs: {}, // handle -> [refcount, name, lib_object]
loadedLibNames: {}, // name -> handle
},
// void* dlopen(const char* filename, int flag);
- dlopen__deps: ['$DLFCN_DATA', '$FS', '$ENV'],
+ dlopen__deps: ['$DLFCN', '$FS', '$ENV'],
dlopen: function(filename, flag) {
// void *dlopen(const char *file, int mode);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html
filename = filename === 0 ? '__self__' : (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename);
- if (DLFCN_DATA.loadedLibNames[filename]) {
+#if ASM_JS
+#if DLOPEN_SUPPORT == 0
+ abort('need to build with DLOPEN_SUPPORT=1 to get dlopen support in asm.js');
+#endif
+#endif
+
+ if (DLFCN.loadedLibNames[filename]) {
// Already loaded; increment ref count and return.
- var handle = DLFCN_DATA.loadedLibNames[filename];
- DLFCN_DATA.loadedLibs[handle].refcount++;
+ var handle = DLFCN.loadedLibNames[filename];
+ DLFCN.loadedLibs[handle].refcount++;
return handle;
}
@@ -5032,7 +5096,7 @@ LibraryManager.library = {
} else {
var target = FS.findObject(filename);
if (!target || target.isFolder || target.isDevice) {
- DLFCN_DATA.errorMsg = 'Could not find dynamic lib: ' + filename;
+ DLFCN.errorMsg = 'Could not find dynamic lib: ' + filename;
return 0;
} else {
FS.forceLoadFile(target);
@@ -5040,19 +5104,26 @@ LibraryManager.library = {
}
try {
- var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length);
+ var lib_module = eval(lib_data)(
+#if ASM_JS
+ DLFCN.functionTable.length,
+#else
+ {{{ Functions.getTable('x') }}}.length,
+#endif
+ Module
+ );
} catch (e) {
#if ASSERTIONS
Module.printErr('Error in loading dynamic library: ' + e);
#endif
- DLFCN_DATA.errorMsg = 'Could not evaluate dynamic lib: ' + filename;
+ DLFCN.errorMsg = 'Could not evaluate dynamic lib: ' + filename;
return 0;
}
// Not all browsers support Object.keys().
var handle = 1;
- for (var key in DLFCN_DATA.loadedLibs) {
- if (DLFCN_DATA.loadedLibs.hasOwnProperty(key)) handle++;
+ for (var key in DLFCN.loadedLibs) {
+ if (DLFCN.loadedLibs.hasOwnProperty(key)) handle++;
}
// We don't care about RTLD_NOW and RTLD_LAZY.
@@ -5066,60 +5137,66 @@ LibraryManager.library = {
var cached_functions = {};
}
- DLFCN_DATA.loadedLibs[handle] = {
+ DLFCN.loadedLibs[handle] = {
refcount: 1,
name: filename,
module: lib_module,
cached_functions: cached_functions
};
- DLFCN_DATA.loadedLibNames[filename] = handle;
+ DLFCN.loadedLibNames[filename] = handle;
return handle;
},
// int dlclose(void* handle);
- dlclose__deps: ['$DLFCN_DATA'],
+ dlclose__deps: ['$DLFCN'],
dlclose: function(handle) {
// int dlclose(void *handle);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlclose.html
- if (!DLFCN_DATA.loadedLibs[handle]) {
- DLFCN_DATA.errorMsg = 'Tried to dlclose() unopened handle: ' + handle;
+ if (!DLFCN.loadedLibs[handle]) {
+ DLFCN.errorMsg = 'Tried to dlclose() unopened handle: ' + handle;
return 1;
} else {
- var lib_record = DLFCN_DATA.loadedLibs[handle];
+ var lib_record = DLFCN.loadedLibs[handle];
if (--lib_record.refcount == 0) {
- delete DLFCN_DATA.loadedLibNames[lib_record.name];
- delete DLFCN_DATA.loadedLibs[handle];
+ if (lib_record.module.cleanups) {
+ lib_record.module.cleanups.forEach(function(cleanup) { cleanup() });
+ }
+ delete DLFCN.loadedLibNames[lib_record.name];
+ delete DLFCN.loadedLibs[handle];
}
return 0;
}
},
// void* dlsym(void* handle, const char* symbol);
- dlsym__deps: ['$DLFCN_DATA'],
+ dlsym__deps: ['$DLFCN'],
dlsym: function(handle, symbol) {
// void *dlsym(void *restrict handle, const char *restrict name);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html
symbol = '_' + Pointer_stringify(symbol);
- if (!DLFCN_DATA.loadedLibs[handle]) {
- DLFCN_DATA.errorMsg = 'Tried to dlsym() from an unopened handle: ' + handle;
+ if (!DLFCN.loadedLibs[handle]) {
+ DLFCN.errorMsg = 'Tried to dlsym() from an unopened handle: ' + handle;
return 0;
} else {
- var lib = DLFCN_DATA.loadedLibs[handle];
+ var lib = DLFCN.loadedLibs[handle];
// self-dlopen means that lib.module is not a superset of
// cached_functions, so check the latter first
if (lib.cached_functions.hasOwnProperty(symbol)) {
return lib.cached_functions[symbol];
} else {
if (!lib.module.hasOwnProperty(symbol)) {
- DLFCN_DATA.errorMsg = ('Tried to lookup unknown symbol "' + symbol +
+ DLFCN.errorMsg = ('Tried to lookup unknown symbol "' + symbol +
'" in dynamic lib: ' + lib.name);
return 0;
} else {
var result = lib.module[symbol];
if (typeof result == 'function') {
- {{{ Functions.getTable('x') }}}.push(result);
- {{{ Functions.getTable('x') }}}.push(0);
- result = {{{ Functions.getTable('x') }}}.length - 2;
+#if ASM_JS
+ result = lib.module.SYMBOL_TABLE[symbol];
+ assert(result);
+#else
+ result = Runtime.addFunction(result);
+#endif
lib.cached_functions = result;
}
return result;
@@ -5128,18 +5205,18 @@ LibraryManager.library = {
}
},
// char* dlerror(void);
- dlerror__deps: ['$DLFCN_DATA'],
+ dlerror__deps: ['$DLFCN'],
dlerror: function() {
// char *dlerror(void);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlerror.html
- if (DLFCN_DATA.errorMsg === null) {
+ if (DLFCN.errorMsg === null) {
return 0;
} else {
- if (DLFCN_DATA.error) _free(DLFCN_DATA.error);
- var msgArr = intArrayFromString(DLFCN_DATA.errorMsg);
- DLFCN_DATA.error = allocate(msgArr, 'i8', ALLOC_NORMAL);
- DLFCN_DATA.errorMsg = null;
- return DLFCN_DATA.error;
+ if (DLFCN.error) _free(DLFCN.error);
+ var msgArr = intArrayFromString(DLFCN.errorMsg);
+ DLFCN.error = allocate(msgArr, 'i8', ALLOC_NORMAL);
+ DLFCN.errorMsg = null;
+ return DLFCN.error;
}
},
@@ -5154,6 +5231,37 @@ LibraryManager.library = {
},
// ==========================================================================
+ // termios.h
+ // ==========================================================================
+ tcgetattr: function(fildes, termios_p) {
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/tcgetattr.html
+ var stream = FS.getStream(fildes);
+ if (!stream) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+ if (!stream.tty) {
+ ___setErrNo(ERRNO_CODES.ENOTTY);
+ return -1;
+ }
+ return 0;
+ },
+
+ tcsetattr: function(fildes, optional_actions, termios_p) {
+ // http://pubs.opengroup.org/onlinepubs/7908799/xsh/tcsetattr.html
+ var stream = FS.getStream(fildes);
+ if (!stream) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+ if (!stream.tty) {
+ ___setErrNo(ERRNO_CODES.ENOTTY);
+ return -1;
+ }
+ return 0;
+ },
+
+ // ==========================================================================
// time.h
// ==========================================================================
@@ -6876,7 +6984,6 @@ LibraryManager.library = {
// ==========================================================================
// arpa/inet.h
// ==========================================================================
-
htonl: function(value) {
return ((value & 0xff) << 24) + ((value & 0xff00) << 8) +
((value & 0xff0000) >>> 8) + ((value & 0xff000000) >>> 24);
@@ -6887,75 +6994,43 @@ LibraryManager.library = {
ntohl: 'htonl',
ntohs: 'htons',
- // http://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntop.html
- inet_ntop__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_ntop4', 'inet_ntop6'],
- inet_ntop: function(af, src, dst, size) {
- switch (af) {
- case {{{ cDefine('AF_INET') }}}:
- return _inet_ntop4(src, dst, size);
- case {{{ cDefine('AF_INET6') }}}:
- return _inet_ntop6(src, dst, size);
- default:
- ___setErrNo(ERRNO_CODES.EAFNOSUPPORT);
- return 0;
- }
- },
- inet_pton__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_pton4', 'inet_pton6'],
- inet_pton: function(af, src, dst) {
- switch (af) {
- case {{{ cDefine('AF_INET') }}}:
- return _inet_pton4(src, dst);
- case {{{ cDefine('AF_INET6') }}}:
- return _inet_pton6(src, dst);
- default:
- ___setErrNo(ERRNO_CODES.EAFNOSUPPORT);
- return -1;
- }
- },
+ // old ipv4 only functions
+ inet_addr__deps: ['_inet_pton4_raw'],
inet_addr: function(ptr) {
- var b = Pointer_stringify(ptr).split(".");
- if (b.length !== 4) return -1; // we return -1 for error, and otherwise a uint32. this helps inet_pton differentiate
- return (Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24)) >>> 0;
- },
- _inet_aton_raw: function(str) {
- var b = str.split(".");
- return (Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24)) >>> 0;
- },
- _inet_ntoa_raw: function(addr) {
- return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff)
+ var addr = __inet_pton4_raw(Pointer_stringify(ptr));
+ if (addr === null) {
+ return -1;
+ }
+ return addr;
},
- inet_ntoa__deps: ['_inet_ntoa_raw'],
+ inet_ntoa__deps: ['_inet_ntop4_raw'],
inet_ntoa: function(in_addr) {
if (!_inet_ntoa.buffer) {
_inet_ntoa.buffer = _malloc(1024);
}
- var addr = getValue(in_addr, 'i32');
- var str = __inet_ntoa_raw(addr);
+ var addr = {{{ makeGetValue('in_addr', '0', 'i32') }}};
+ var str = __inet_ntop4_raw(addr);
writeStringToMemory(str.substr(0, 1024), _inet_ntoa.buffer);
return _inet_ntoa.buffer;
},
- inet_aton__deps: ['inet_addr'],
+ inet_aton__deps: ['_inet_pton4_raw'],
inet_aton: function(cp, inp) {
- var addr = _inet_addr(cp);
- setValue(inp, addr, 'i32');
- if (addr < 0) return 0;
- return 1;
- },
-
- inet_ntop4__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_ntoa_raw'],
- inet_ntop4: function(src, dst, size) {
- var str = __inet_ntoa_raw(getValue(src, 'i32'));
- if (str.length+1 > size) {
- ___setErrNo(ERRNO_CODES.ENOSPC);
+ var addr = __inet_pton4_raw(Pointer_stringify(cp));
+ if (addr === null) {
return 0;
}
- writeStringToMemory(str, dst);
- return dst;
+ {{{ makeSetValue('inp', '0', 'addr', 'i32') }}}
+ return 1;
},
- inet_ntop6__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_ntop6_raw'],
- inet_ntop6: function(src, dst, size) {
- var str = _inet_ntop6_raw(src);
+ // new ipv4 / ipv6 functions
+ _inet_ntop4_raw: function(addr) {
+ return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff)
+ },
+ _inet_ntop4__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_ntop4_raw'],
+ _inet_ntop4: function(src, dst, size) {
+ var addr = {{{ makeGetValue('src', '0', 'i32') }}};
+ var str = __inet_ntop4_raw(addr);
if (str.length+1 > size) {
___setErrNo(ERRNO_CODES.ENOSPC);
return 0;
@@ -6963,9 +7038,8 @@ LibraryManager.library = {
writeStringToMemory(str, dst);
return dst;
},
- inet_ntop6_raw__deps: ['ntohs'],
- inet_ntop6_raw: function(src) {
-
+ _inet_ntop6_raw__deps: ['ntohs', '_inet_ntop4_raw'],
+ _inet_ntop6_raw: function(ints) {
// ref: http://www.ietf.org/rfc/rfc2373.txt - section 2.5.4
// Format for IPv4 compatible and mapped 128-bit IPv6 Addresses
// 128-bits are split into eight 16-bit words
@@ -6980,7 +7054,6 @@ LibraryManager.library = {
// +--------------------------------------+----+---------------------+
// |0000..............................0000|FFFF| IPv4 ADDRESS | (mapped)
// +--------------------------------------+----+---------------------+
-
var str = "";
var word = 0;
var longest = 0;
@@ -6988,27 +7061,37 @@ LibraryManager.library = {
var zstart = 0;
var len = 0;
var i = 0;
+ var parts = [
+ ints[0] & 0xffff,
+ (ints[0] >> 16),
+ ints[1] & 0xffff,
+ (ints[1] >> 16),
+ ints[2] & 0xffff,
+ (ints[2] >> 16),
+ ints[3] & 0xffff,
+ (ints[3] >> 16)
+ ];
// Handle IPv4-compatible, IPv4-mapped, loopback and any/unspecified addresses
var hasipv4 = true;
var v4part = "";
// check if the 10 high-order bytes are all zeros (first 5 words)
- for (i = 0; i < 10; i++) {
- if ({{{ makeGetValue('src', 'i', 'i8') }}} !== 0) { hasipv4 = false; break; }
+ for (i = 0; i < 5; i++) {
+ if (parts[i] !== 0) { hasipv4 = false; break; }
}
if (hasipv4) {
// low-order 32-bits store an IPv4 address (bytes 13 to 16) (last 2 words)
- v4part = __inet_ntoa_raw({{{ makeGetValue('src', '12', 'i32') }}});
+ v4part = __inet_ntop4_raw(parts[6] | (parts[7] << 16));
// IPv4-mapped IPv6 address if 16-bit value (bytes 11 and 12) == 0xFFFF (6th word)
- if ({{{ makeGetValue('src', '10', 'i16') }}} === -1) {
+ if (parts[5] === -1) {
str = "::ffff:";
str += v4part;
return str;
}
// IPv4-compatible IPv6 address if 16-bit value (bytes 11 and 12) == 0x0000 (6th word)
- if ({{{ makeGetValue('src', '10', 'i16') }}} === 0) {
+ if (parts[5] === 0) {
str = "::";
//special case IPv6 addresses
if(v4part === "0.0.0.0") v4part = ""; // any/unspecified address
@@ -7022,7 +7105,7 @@ LibraryManager.library = {
// first run to find the longest contiguous zero words
for (word = 0; word < 8; word++) {
- if ({{{ makeGetValue('src', 'word*2', 'i16') }}} === 0) {
+ if (parts[word] === 0) {
if (word - lastzero > 1) {
len = 0;
}
@@ -7038,7 +7121,7 @@ LibraryManager.library = {
for (word = 0; word < 8; word++) {
if (longest > 1) {
// compress contiguous zeros - to produce "::"
- if ({{{ makeGetValue('src', 'word*2', 'i16') }}} === 0 && word >= zstart && word < (zstart + longest) ) {
+ if (parts[word] === 0 && word >= zstart && word < (zstart + longest) ) {
if (word === zstart) {
str += ":";
if (zstart === 0) str += ":"; //leading zeros case
@@ -7047,54 +7130,86 @@ LibraryManager.library = {
}
}
// converts 16-bit words from big-endian to little-endian before converting to hex string
- str += Number(_ntohs({{{ makeGetValue('src', 'word*2', 'i16') }}} & 0xffff)).toString(16);
+ str += Number(_ntohs(parts[word] & 0xffff)).toString(16);
str += word < 7 ? ":" : "";
}
return str;
},
-
- inet_pton4__deps: ['inet_addr'],
- inet_pton4: function(src, dst) {
- var ret = _inet_addr(src);
- if (ret === -1 || isNaN(ret)) return 0;
- setValue(dst, ret, 'i32');
- return 1;
+ _inet_ntop6__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_ntop6_raw'],
+ _inet_ntop6: function(src, dst, size) {
+ var addr = [
+ {{{ makeGetValue('src', '0', 'i32') }}}, {{{ makeGetValue('src', '4', 'i32') }}},
+ {{{ makeGetValue('src', '8', 'i32') }}}, {{{ makeGetValue('src', '12', 'i32') }}}
+ ];
+ var str = __inet_ntop6_raw(addr);
+ if (str.length+1 > size) {
+ ___setErrNo(ERRNO_CODES.ENOSPC);
+ return 0;
+ }
+ writeStringToMemory(str, dst);
+ return dst;
},
-
- inet_pton6__deps: ['inet_pton6_raw'],
- inet_pton6: function(src, dst) {
- return _inet_pton6_raw(Pointer_stringify(src), dst);
+ inet_ntop__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_ntop4', '_inet_ntop6'],
+ inet_ntop: function(af, src, dst, size) {
+ // http://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntop.html
+ switch (af) {
+ case {{{ cDefine('AF_INET') }}}:
+ return __inet_ntop4(src, dst, size);
+ case {{{ cDefine('AF_INET6') }}}:
+ return __inet_ntop6(src, dst, size);
+ default:
+ ___setErrNo(ERRNO_CODES.EAFNOSUPPORT);
+ return 0;
+ }
},
- inet_pton6_raw__deps: ['htons'],
- inet_pton6_raw: function(addr, dst) {
+ _inet_pton4_raw: function(str) {
+ var b = str.split('.');
+ for (var i = 0; i < 4; i++) {
+ var tmp = Number(b[i]);
+ if (isNaN(tmp)) return null;
+ b[i] = tmp;
+ }
+ return (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)) >>> 0;
+ },
+ _inet_pton4__deps: ['_inet_pton4_raw'],
+ _inet_pton4: function(src, dst) {
+ var ret = __inet_pton4_raw(Pointer_stringify(src));
+ if (ret === null) {
+ return 0;
+ }
+ {{{ makeSetValue('dst', '0', 'ret', 'i32') }}}
+ return 1;
+ },
+ _inet_pton6_raw__deps: ['htons'],
+ _inet_pton6_raw: function(str) {
var words;
var w, offset, z, i;
/* http://home.deds.nl/~aeron/regex/ */
var valid6regx = /^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i
- if (!valid6regx.test(addr)) {
- return 0;
+ var parts = [];
+ if (!valid6regx.test(str)) {
+ return null;
}
- if (addr === "::") {
- for (i=0; i < 4; i++) {{{ makeSetValue('dst', 'i*4', '0', 'i32') }}};
- return 1;
+ if (str === "::") {
+ return [0, 0, 0, 0, 0, 0, 0, 0];
}
// Z placeholder to keep track of zeros when splitting the string on ":"
- if (addr.indexOf("::") === 0) {
- addr = addr.replace("::", "Z:"); // leading zeros case
+ if (str.indexOf("::") === 0) {
+ str = str.replace("::", "Z:"); // leading zeros case
} else {
- addr = addr.replace("::", ":Z:");
+ str = str.replace("::", ":Z:");
}
- if (addr.indexOf(".") > 0) {
- // parse IPv4 embedded address
- addr = addr.replace(new RegExp('[.]', 'g'), ":");
- words = addr.split(":");
+ if (str.indexOf(".") > 0) {
+ // parse IPv4 embedded stress
+ str = str.replace(new RegExp('[.]', 'g'), ":");
+ words = str.split(":");
words[words.length-4] = parseInt(words[words.length-4]) + parseInt(words[words.length-3])*256;
words[words.length-3] = parseInt(words[words.length-2]) + parseInt(words[words.length-1])*256;
words = words.slice(0, words.length-2);
} else {
- words = addr.split(":");
+ words = str.split(":");
}
offset = 0; z = 0;
@@ -7103,22 +7218,69 @@ LibraryManager.library = {
if (words[w] === 'Z') {
// compressed zeros - write appropriate number of zero words
for (z = 0; z < (8 - words.length+1); z++) {
- {{{ makeSetValue('dst', '(w+z)*2', '0', 'i16') }}};
+ parts[w+z] = 0;
}
offset = z-1;
} else {
// parse hex to field to 16-bit value and write it in network byte-order
- {{{ makeSetValue('dst', '(w+offset)*2', '_htons(parseInt(words[w],16))', 'i16') }}};
+ parts[w+offset] = _htons(parseInt(words[w],16));
}
} else {
// parsed IPv4 words
- {{{ makeSetValue('dst', '(w+offset)*2', 'words[w]', 'i16') }}};
+ parts[w+offset] = words[w];
}
}
+ return [
+ (parts[1] << 16) | parts[0],
+ (parts[3] << 16) | parts[2],
+ (parts[5] << 16) | parts[4],
+ (parts[7] << 16) | parts[6]
+ ];
+ },
+ _inet_pton6__deps: ['_inet_pton6_raw'],
+ _inet_pton6: function(src, dst) {
+ var ints = __inet_pton6_raw(Pointer_stringify(src));
+ if (ints === null) {
+ return 0;
+ }
+ for (var i = 0; i < 4; i++) {
+ {{{ makeSetValue('dst', 'i*4', 'ints[i]', 'i32') }}};
+ }
return 1;
},
+ inet_pton__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_pton4', '_inet_pton6'],
+ inet_pton: function(af, src, dst) {
+ // http://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_pton.html
+ switch (af) {
+ case {{{ cDefine('AF_INET') }}}:
+ return __inet_pton4(src, dst);
+ case {{{ cDefine('AF_INET6') }}}:
+ return __inet_pton6(src, dst);
+ default:
+ ___setErrNo(ERRNO_CODES.EAFNOSUPPORT);
+ return -1;
+ }
+ },
+
+ // ==========================================================================
+ // net/if.h
+ // ==========================================================================
+
+ if_nametoindex: function(a) {
+ return 0;
+ },
+ if_indextoname: function(a, b) {
+ return 0;
+ },
+ if_nameindex: function() {
+ return 0;
+ },
+ if_freenameindex: function(a) {
+ },
+ // ==========================================================================
// netinet/in.h
+ // ==========================================================================
_in6addr_any:
'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)',
@@ -7139,10 +7301,56 @@ LibraryManager.library = {
// netdb.h
// ==========================================================================
- // All we can do is alias names to ips. you give this a name, it returns an
- // "ip" that we later know to use as a name. There is no way to do actual
- // name resolving clientside in a browser.
- // we do the aliasing in 172.29.*.*, giving us 65536 possibilities
+ // We can't actually resolve hostnames in the browser, so instead
+ // we're generating fake IP addresses with lookup_name that we can
+ // resolve later on with lookup_addr.
+ // We do the aliasing in 172.29.*.*, giving us 65536 possibilities.
+ $DNS: {
+ address_map: {
+ id: 1,
+ addrs: {},
+ names: {}
+ },
+
+ lookup_name__deps: ['_inet_pton4_raw', '_inet_pton6_raw'],
+ lookup_name: function (name) {
+ // If the name is already a valid ipv4 / ipv6 address, don't generate a fake one.
+ var res = __inet_pton4_raw(name);
+ if (res) {
+ return name;
+ }
+ res = __inet_pton6_raw(name);
+ if (res) {
+ return name;
+ }
+
+ // See if this name is already mapped.
+ var addr;
+
+ if (DNS.address_map.addrs[name]) {
+ addr = DNS.address_map.addrs[name];
+ } else {
+ var id = DNS.address_map.id++;
+ assert(id < 65535, 'exceeded max address mappings of 65535');
+
+ addr = '172.29.' + (id & 0xff) + '.' + (id & 0xff00);
+
+ DNS.address_map.names[addr] = name;
+ DNS.address_map.addrs[name] = addr;
+ }
+
+ return addr;
+ },
+
+ lookup_addr: function (addr) {
+ if (DNS.address_map.names[addr]) {
+ return DNS.address_map.names[addr];
+ }
+
+ return null;
+ }
+ },
+
// note: lots of leaking here!
__hostent_struct_layout: Runtime.generateStructInfo([
['i8*', 'h_name'],
@@ -7152,44 +7360,273 @@ LibraryManager.library = {
['i8**', 'h_addr_list'],
]),
- gethostbyname__deps: ['__hostent_struct_layout'],
+ _addrinfo_layout: Runtime.generateStructInfo([
+ ['i32', 'ai_flags'],
+ ['i32', 'ai_family'],
+ ['i32', 'ai_socktype'],
+ ['i32', 'ai_protocol'],
+ ['i32', 'ai_addrlen'],
+ ['*', 'ai_addr'],
+ ['*', 'ai_canonname'],
+ ['*', 'ai_next']
+ ]),
+
+ gethostbyaddr__deps: ['$DNS', 'gethostbyname', '_inet_ntop4_raw'],
+ gethostbyaddr: function (addr, addrlen, type) {
+ if (type !== {{{ cDefine('AF_INET') }}}) {
+ ___setErrNo(ERRNO_CODES.EAFNOSUPPORT);
+ return null;
+ }
+ addr = {{{ makeGetValue('addr', '0', 'i32') }}}; // addr is in_addr
+ var host = __inet_ntop4_raw(addr);
+ var lookup = DNS.lookup_addr(host);
+ if (lookup) {
+ host = lookup;
+ }
+ var hostp = allocate(intArrayFromString(host), 'i8', ALLOC_STACK);
+ return _gethostbyname(hostp);
+ },
+
+ gethostbyname__deps: ['$DNS', '__hostent_struct_layout', '_inet_pton4_raw'],
gethostbyname: function(name) {
name = Pointer_stringify(name);
- if (!_gethostbyname.id) {
- _gethostbyname.id = 1;
- _gethostbyname.table = {};
- }
- var id = _gethostbyname.id++;
- assert(id < 65535);
- var fakeAddr = 172 | (29 << 8) | ((id & 0xff) << 16) | ((id & 0xff00) << 24);
- _gethostbyname.table[id] = name;
+
// generate hostent
- var ret = _malloc(___hostent_struct_layout.__size__);
+ var ret = _malloc(___hostent_struct_layout.__size__); // XXX possibly leaked, as are others here
var nameBuf = _malloc(name.length+1);
writeStringToMemory(name, nameBuf);
- setValue(ret+___hostent_struct_layout.h_name, nameBuf, 'i8*');
+ {{{ makeSetValue('ret', '___hostent_struct_layout.h_name', 'nameBuf', 'i8*') }}}
var aliasesBuf = _malloc(4);
- setValue(aliasesBuf, 0, 'i8*');
- setValue(ret+___hostent_struct_layout.h_aliases, aliasesBuf, 'i8**');
- setValue(ret+___hostent_struct_layout.h_addrtype, {{{ cDefine('AF_INET') }}}, 'i32');
- setValue(ret+___hostent_struct_layout.h_length, 4, 'i32');
+ {{{ makeSetValue('aliasesBuf', '0', '0', 'i8*') }}}
+ {{{ makeSetValue('ret', '___hostent_struct_layout.h_aliases', 'aliasesBuf', 'i8**') }}}
+ var afinet = {{{ cDefine("AF_INET") }}};
+ {{{ makeSetValue('ret', '___hostent_struct_layout.h_addrtype', 'afinet', 'i32') }}}
+ {{{ makeSetValue('ret', '___hostent_struct_layout.h_length', '4', 'i32') }}}
var addrListBuf = _malloc(12);
- setValue(addrListBuf, addrListBuf+8, 'i32*');
- setValue(addrListBuf+4, 0, 'i32*');
- setValue(addrListBuf+8, fakeAddr, 'i32');
- setValue(ret+___hostent_struct_layout.h_addr_list, addrListBuf, 'i8**');
+ {{{ makeSetValue('addrListBuf', '0', 'addrListBuf+8', 'i32*') }}}
+ {{{ makeSetValue('addrListBuf', '4', '0', 'i32*') }}}
+ {{{ makeSetValue('addrListBuf', '8', '__inet_pton4_raw(DNS.lookup_name(name))', 'i32') }}}
+ {{{ makeSetValue('ret', '___hostent_struct_layout.h_addr_list', 'addrListBuf', 'i8**') }}}
return ret;
},
gethostbyname_r__deps: ['gethostbyname'],
- gethostbyname_r: function(name, hostData, buffer, bufferSize, hostEntry, errnum) {
+ gethostbyname_r: function(name, ret, buf, buflen, err) {
var data = _gethostbyname(name);
- _memcpy(hostData, data, ___hostent_struct_layout.__size__);
+ _memcpy(ret, data, ___hostent_struct_layout.__size__);
_free(data);
- setValue(errnum, 0, 'i32');
+ {{{ makeSetValue('err', '0', '0', 'i32') }}};
+ return ret;
+ },
+
+ getaddrinfo__deps: ['$Sockets', '$DNS', '_addrinfo_layout', '_inet_pton4_raw', '_inet_ntop4_raw', '_inet_pton6_raw', '_inet_ntop6_raw', '_write_sockaddr', 'htonl'],
+ getaddrinfo: function(node, service, hint, out) {
+ var addrs = [];
+ var canon = null;
+ var addr = 0;
+ var port = 0;
+ var flags = 0;
+ var family = {{{ cDefine('AF_UNSPEC') }}};
+ var type = 0;
+ var proto = 0;
+ var ai, last;
+
+ function allocaddrinfo(family, type, proto, canon, addr, port) {
+ var sa, salen, ai;
+ var res;
+
+ salen = family === {{{ cDefine('AF_INET6') }}} ?
+ Sockets.sockaddr_in6_layout.__size__ :
+ Sockets.sockaddr_in_layout.__size__;
+ addr = family === {{{ cDefine('AF_INET6') }}} ?
+ __inet_ntop6_raw(addr) :
+ __inet_ntop4_raw(addr);
+ sa = _malloc(salen);
+ res = __write_sockaddr(sa, family, addr, port);
+ assert(!res.errno);
+
+ ai = _malloc(__addrinfo_layout.__size__);
+ {{{ makeSetValue('ai', '__addrinfo_layout.ai_family', 'family', 'i32') }}};
+ {{{ makeSetValue('ai', '__addrinfo_layout.ai_socktype', 'type', 'i32') }}};
+ {{{ makeSetValue('ai', '__addrinfo_layout.ai_protocol', 'proto', 'i32') }}};
+ if (canon) {
+ {{{ makeSetValue('ai', '__addrinfo_layout.ai_canonname', 'canon', 'i32') }}};
+ }
+ {{{ makeSetValue('ai', '__addrinfo_layout.ai_addr', 'sa', '*') }}};
+ if (family === {{{ cDefine('AF_INET6') }}}) {
+ {{{ makeSetValue('ai', '__addrinfo_layout.ai_addrlen', 'Sockets.sockaddr_in6_layout.__size__', 'i32') }}};
+ } else {
+ {{{ makeSetValue('ai', '__addrinfo_layout.ai_addrlen', 'Sockets.sockaddr_in_layout.__size__', 'i32') }}};
+ }
+
+ return ai;
+ }
+
+ if (hint) {
+ flags = {{{ makeGetValue('hint', '__addrinfo_layout.ai_flags', 'i32') }}};
+ family = {{{ makeGetValue('hint', '__addrinfo_layout.ai_family', 'i32') }}};
+ type = {{{ makeGetValue('hint', '__addrinfo_layout.ai_socktype', 'i32') }}};
+ proto = {{{ makeGetValue('hint', '__addrinfo_layout.ai_protocol', 'i32') }}};
+ }
+ if (type && !proto) {
+ proto = type === {{{ cDefine('SOCK_DGRAM') }}} ? {{{ cDefine('IPPROTO_UDP') }}} : {{{ cDefine('IPPROTO_TCP') }}};
+ }
+ if (!type && proto) {
+ type = proto === {{{ cDefine('IPPROTO_UDP') }}} ? {{{ cDefine('SOCK_DGRAM') }}} : {{{ cDefine('SOCK_STREAM') }}};
+ }
+
+ if (!node && !service) {
+ return {{{ cDefine('EAI_NONAME') }}};
+ }
+ if (flags & ~({{{ cDefine('AI_PASSIVE') }}}|{{{ cDefine('AI_CANONNAME') }}}|{{{ cDefine('AI_NUMERICHOST') }}}|
+ {{{ cDefine('AI_NUMERICSERV') }}}|{{{ cDefine('AI_V4MAPPED') }}}|{{{ cDefine('AI_ALL') }}}|{{{ cDefine('AI_ADDRCONFIG') }}})) {
+ return {{{ cDefine('EAI_BADFLAGS') }}};
+ }
+ if (({{{ makeGetValue('hint', '__addrinfo_layout.ai_flags', 'i32') }}} & {{{ cDefine('AI_CANONNAME') }}}) && !node) {
+ return {{{ cDefine('EAI_BADFLAGS') }}};
+ }
+ if (flags & {{{ cDefine('AI_ADDRCONFIG') }}}) {
+ // TODO
+ return {{{ cDefine('EAI_NONAME') }}};
+ }
+ if (type !== {{{ cDefine('SOCK_STREAM') }}} && type !== {{{ cDefine('SOCK_DGRAM') }}}) {
+ return {{{ cDefine('EAI_SOCKTYPE') }}};
+ }
+ if (family !== {{{ cDefine('AF_UNSPEC') }}} && family !== {{{ cDefine('AF_INET') }}} && family !== {{{ cDefine('AF_INET6') }}}) {
+ return {{{ cDefine('EAI_FAMILY') }}};
+ }
+
+ if (service) {
+ service = Pointer_stringify(service);
+ port = parseInt(service, 10);
+
+ if (isNaN(port)) {
+ if (flags & {{{ cDefine('AI_NUMERICSERV') }}}) {
+ return {{{ cDefine('EAI_NONAME') }}};
+ }
+ // TODO support resolving well-known service names from:
+ // http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt
+ return {{{ cDefine('EAI_SERVICE') }}};
+ }
+ }
+
+ if (!node) {
+ if (family === {{{ cDefine('AF_UNSPEC') }}}) {
+ family = {{{ cDefine('AF_INET') }}};
+ }
+ if ((flags & {{{ cDefine('AI_PASSIVE') }}}) === 0) {
+ if (family === {{{ cDefine('AF_INET') }}}) {
+ addr = _htonl({{{ cDefine('INADDR_LOOPBACK') }}});
+ } else {
+ addr = [0, 0, 0, 1];
+ }
+ }
+ ai = allocaddrinfo(family, type, proto, null, addr, port);
+ {{{ makeSetValue('out', '0', 'ai', '*') }}};
+ return 0;
+ }
+
+ //
+ // try as a numeric address
+ //
+ node = Pointer_stringify(node);
+ addr = __inet_pton4_raw(node);
+ if (addr !== null) {
+ // incoming node is a valid ipv4 address
+ if (family === {{{ cDefine('AF_UNSPEC') }}} || family === {{{ cDefine('AF_INET') }}}) {
+ family = {{{ cDefine('AF_INET') }}};
+ }
+ else if (family === {{{ cDefine('AF_INET6') }}} && (flags & {{{ cDefine('AI_V4MAPPED') }}})) {
+ addr = [0, 0, _htonl(0xffff), addr];
+ family = {{{ cDefine('AF_INET6') }}};
+ } else {
+ return {{{ cDefine('EAI_NONAME') }}};
+ }
+ } else {
+ addr = __inet_pton6_raw(node);
+ if (addr !== null) {
+ // incoming node is a valid ipv6 address
+ if (family === {{{ cDefine('AF_UNSPEC') }}} || family === {{{ cDefine('AF_INET6') }}}) {
+ family = {{{ cDefine('AF_INET6') }}};
+ } else {
+ return {{{ cDefine('EAI_NONAME') }}};
+ }
+ }
+ }
+ if (addr != null) {
+ ai = allocaddrinfo(family, type, proto, node, addr, port);
+ {{{ makeSetValue('out', '0', 'ai', '*') }}};
+ return 0;
+ }
+ if (flags & {{{ cDefine('AI_NUMERICHOST') }}}) {
+ return {{{ cDefine('EAI_NONAME') }}};
+ }
+
+ //
+ // try as a hostname
+ //
+ // resolve the hostname to a temporary fake address
+ node = DNS.lookup_name(node);
+ addr = __inet_pton4_raw(node);
+ if (family === {{{ cDefine('AF_UNSPEC') }}}) {
+ family = {{{ cDefine('AF_INET') }}}
+ } else if (family === {{{ cDefine('AF_INET6') }}}) {
+ addr = [0, 0, _htonl(0xffff), addr];
+ }
+ ai = allocaddrinfo(family, type, proto, null, addr, port);
+ {{{ makeSetValue('out', '0', 'ai', '*') }}};
+ return 0;
+ },
+
+ freeaddrinfo__deps: ['$Sockets', '_addrinfo_layout'],
+ freeaddrinfo: function(ai) {
+ var sa = {{{ makeGetValue('ai', '__addrinfo_layout.ai_addr', '*') }}};
+ _free(sa);
+ _free(ai);
+ },
+
+ getnameinfo__deps: ['$Sockets', '$DNS', '__hostent_struct_layout', '_read_sockaddr'],
+ getnameinfo: function (sa, salen, node, nodelen, serv, servlen, flags) {
+ var info = __read_sockaddr(sa, salen);
+ if (info.errno) {
+ return {{{ cDefine('EAI_FAMILY') }}};
+ }
+ var port = info.port;
+ var addr = info.addr;
+
+ if (node && nodelen) {
+ var lookup;
+ if ((flags & {{{ cDefine('NI_NUMERICHOST') }}}) || !(lookup = DNS.lookup_addr(addr))) {
+ if (flags & {{{ cDefine('NI_NAMEREQD') }}}) {
+ return {{{ cDefine('EAI_NONAME') }}};
+ }
+ } else {
+ addr = lookup;
+ }
+ if (addr.length >= nodelen) {
+ return {{{ cDefine('EAI_OVERFLOW') }}};
+ }
+ writeStringToMemory(addr, node);
+ }
+
+ if (serv && servlen) {
+ port = '' + port;
+ if (port.length > servlen) {
+ return {{{ cDefine('EAI_OVERFLOW') }}};
+ }
+ writeStringToMemory(port, serv);
+ }
+
return 0;
},
+ gai_strerror: function(val) {
+ if (!_gai_strerror.error) {
+ _gai_strerror.error = allocate(intArrayFromString("unknown error"), 'i8', ALLOC_NORMAL);
+ }
+ return _gai_strerror.error;
+ },
+
// ==========================================================================
// sockets. Note that the implementation assumes all sockets are always
// nonblocking
@@ -7223,6 +7660,13 @@ LibraryManager.library = {
['i32', 'sin_zero'],
['i16', 'sin_zero_b'],
]),
+ sockaddr_in6_layout: Runtime.generateStructInfo([
+ ['i32', 'sin6_family'],
+ ['i16', 'sin6_port'],
+ ['i32', 'sin6_flowinfo'],
+ ['b16', 'sin6_addr'],
+ ['i32', 'sin6_scope_id']
+ ]),
msghdr_layout: Runtime.generateStructInfo([
['*', 'msg_name'],
['i32', 'msg_namelen'],
@@ -7232,6 +7676,10 @@ LibraryManager.library = {
['i32', 'msg_controllen'],
['i32', 'msg_flags'],
]),
+ iovec_layout: Runtime.generateStructInfo([
+ ['i8*', 'iov_base'],
+ ['i32', 'iov_len']
+ ])
},
#if SOCKET_WEBRTC
@@ -7390,7 +7838,7 @@ LibraryManager.library = {
// Stub: connection-oriented sockets are not supported yet.
},
- bind__deps: ['$FS', '$Sockets', '_inet_ntoa_raw', 'ntohs', 'mkport'],
+ bind__deps: ['$FS', '$Sockets', '_inet_ntop4_raw', 'ntohs', 'mkport'],
bind: function(fd, addr, addrlen) {
var info = FS.getStream(fd);
if (!info) return -1;
@@ -7402,7 +7850,7 @@ LibraryManager.library = {
info.port = _mkport();
}
info.addr = Sockets.localAddr; // 10.0.0.254
- info.host = __inet_ntoa_raw(info.addr);
+ info.host = __inet_ntop4_raw(info.addr);
info.close = function() {
Sockets.portmap[info.port] = undefined;
}
@@ -7411,7 +7859,7 @@ LibraryManager.library = {
info.bound = true;
},
- sendmsg__deps: ['$FS', '$Sockets', 'bind', '_inet_ntoa_raw', 'ntohs'],
+ sendmsg__deps: ['$FS', '$Sockets', 'bind', '_inet_ntop4_raw', 'ntohs'],
sendmsg: function(fd, msg, flags) {
var info = FS.getStream(fd);
if (!info) return -1;
@@ -7425,7 +7873,7 @@ LibraryManager.library = {
var port = _ntohs(getValue(name + Sockets.sockaddr_in_layout.sin_port, 'i16'));
var addr = getValue(name + Sockets.sockaddr_in_layout.sin_addr, 'i32');
var connection = Sockets.connections[addr];
- // var host = __inet_ntoa_raw(addr);
+ // var host = __inet_ntop4_raw(addr);
if (!(connection && connection.connected)) {
___setErrNo(ERRNO_CODES.EWOULDBLOCK);
@@ -7616,425 +8064,529 @@ LibraryManager.library = {
}
},
#else
- socket__deps: ['$FS', '$Sockets'],
+ // ==========================================================================
+ // socket.h
+ // ==========================================================================
+ _read_sockaddr__deps: ['$Sockets', '_inet_ntop4_raw', '_inet_ntop6_raw'],
+ _read_sockaddr: function (sa, salen) {
+ // family / port offsets are common to both sockaddr_in and sockaddr_in6
+ var family = {{{ makeGetValue('sa', 'Sockets.sockaddr_in_layout.sin_family', 'i32') }}};
+ var port = _ntohs({{{ makeGetValue('sa', 'Sockets.sockaddr_in_layout.sin_port', 'i16') }}});
+ var addr;
+
+ switch (family) {
+ case {{{ cDefine('AF_INET') }}}:
+ if (salen !== Sockets.sockaddr_in_layout.__size__) {
+ return { errno: ERRNO_CODES.EINVAL };
+ }
+ addr = {{{ makeGetValue('sa', 'Sockets.sockaddr_in_layout.sin_addr', 'i32') }}};
+ addr = __inet_ntop4_raw(addr);
+ break;
+ case {{{ cDefine('AF_INET6') }}}:
+ if (salen !== Sockets.sockaddr_in6_layout.__size__) {
+ return { errno: ERRNO_CODES.EINVAL };
+ }
+ addr = [
+ {{{ makeGetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+0', 'i32') }}},
+ {{{ makeGetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+4', 'i32') }}},
+ {{{ makeGetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+8', 'i32') }}},
+ {{{ makeGetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+12', 'i32') }}}
+ ];
+ addr = __inet_ntop6_raw(addr);
+ break;
+ default:
+ return { errno: ERRNO_CODES.EAFNOSUPPORT };
+ }
+
+ return { family: family, addr: addr, port: port };
+ },
+ _write_sockaddr__deps: ['$Sockets', '_inet_pton4_raw', '_inet_pton6_raw'],
+ _write_sockaddr: function (sa, family, addr, port) {
+ switch (family) {
+ case {{{ cDefine('AF_INET') }}}:
+ addr = __inet_pton4_raw(addr);
+ {{{ makeSetValue('sa', 'Sockets.sockaddr_in_layout.sin_family', 'family', 'i32') }}};
+ {{{ makeSetValue('sa', 'Sockets.sockaddr_in_layout.sin_addr', 'addr', 'i32') }}};
+ {{{ makeSetValue('sa', 'Sockets.sockaddr_in_layout.sin_port', '_htons(port)', 'i16') }}};
+ break;
+ case {{{ cDefine('AF_INET6') }}}:
+ addr = __inet_pton6_raw(addr);
+ {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_family', 'family', 'i32') }}};
+ {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+0', 'addr[0]', 'i32') }}};
+ {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+4', 'addr[1]', 'i32') }}};
+ {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+8', 'addr[2]', 'i32') }}};
+ {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+12', 'addr[3]', 'i32') }}};
+ {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_port', '_htons(port)', 'i16') }}};
+ break;
+ default:
+ return { errno: ERRNO_CODES.EAFNOSUPPORT };
+ }
+ // kind of lame, but let's match _read_sockaddr's interface
+ return {};
+ },
+
+ socket__deps: ['$FS', '$SOCKFS'],
socket: function(family, type, protocol) {
- var stream = type == {{{ cDefine('SOCK_STREAM') }}};
- if (protocol) {
- assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if SOCK_STREAM, must be tcp
+ var sock = SOCKFS.createSocket(family, type, protocol);
+ assert(sock.stream.fd < 64); // select() assumes socket fd values are in 0..63
+ return sock.stream.fd;
+ },
+
+ socketpair__deps: ['$ERRNO_CODES', '__setErrNo'],
+ socketpair: function(domain, type, protocol, sv) {
+ // int socketpair(int domain, int type, int protocol, int sv[2]);
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/socketpair.html
+ ___setErrNo(ERRNO_CODES.EOPNOTSUPP);
+ return -1;
+ },
+
+ shutdown__deps: ['$SOCKFS', '$ERRNO_CODES', '__setErrNo'],
+ shutdown: function(fd, how) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- var stream = FS.createStream({
- connected: false,
- stream: stream,
- socket: true,
- stream_ops: {}
- });
- assert(stream.fd < 64); // select() assumes socket fd values are in 0..63
- return stream.fd;
+ _close(fd);
},
- connect__deps: ['$FS', '$Sockets', '_inet_ntoa_raw', 'ntohs', 'gethostbyname'],
- connect: function(fd, addr, addrlen) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- info.connected = true;
- info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32');
- info.port = _htons(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16'));
- info.host = __inet_ntoa_raw(info.addr);
- // Support 'fake' ips from gethostbyname
- var parts = info.host.split('.');
- if (parts[0] == '172' && parts[1] == '29') {
- var low = Number(parts[2]);
- var high = Number(parts[3]);
- info.host = _gethostbyname.table[low + 0xff*high];
- assert(info.host, 'problem translating fake ip ' + parts);
+ bind__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_read_sockaddr'],
+ bind: function(fd, addrp, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- try {
- console.log('opening ws://' + info.host + ':' + info.port);
- info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']);
- info.socket.binaryType = 'arraybuffer';
- var i32Temp = new Uint32Array(1);
- var i8Temp = new Uint8Array(i32Temp.buffer);
+ var info = __read_sockaddr(addrp, addrlen);
+ if (info.errno) {
+ ___setErrNo(info.errno);
+ return -1;
+ }
+ var port = info.port;
+ var addr = DNS.lookup_addr(info.addr) || info.addr;
- info.inQueue = [];
- info.hasData = function() { return info.inQueue.length > 0 }
- if (!info.stream) {
- var partialBuffer = null; // in datagram mode, inQueue contains full dgram messages; this buffers incomplete data. Must begin with the beginning of a message
- }
+ try {
+ sock.sock_ops.bind(sock, addr, port);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
+ },
- info.socket.onmessage = function(event) {
- assert(typeof event.data !== 'string' && event.data.byteLength); // must get binary data!
- var data = new Uint8Array(event.data); // make a typed array view on the array buffer
-#if SOCKET_DEBUG
- Module.print(['onmessage', data.length, '|', Array.prototype.slice.call(data)]);
-#endif
- if (info.stream) {
- info.inQueue.push(data);
- } else {
- // we added headers with message sizes, read those to find discrete messages
- if (partialBuffer) {
- // append to the partial buffer
- var newBuffer = new Uint8Array(partialBuffer.length + data.length);
- newBuffer.set(partialBuffer);
- newBuffer.set(data, partialBuffer.length);
- // forget the partial buffer and work on data
- data = newBuffer;
- partialBuffer = null;
- }
- var currPos = 0;
- while (currPos+4 < data.length) {
- i8Temp.set(data.subarray(currPos, currPos+4));
- var currLen = i32Temp[0];
- assert(currLen > 0);
- if (currPos + 4 + currLen > data.length) {
- break; // not enough data has arrived
- }
- currPos += 4;
-#if SOCKET_DEBUG
- Module.print(['onmessage message', currLen, '|', Array.prototype.slice.call(data.subarray(currPos, currPos+currLen))]);
-#endif
- info.inQueue.push(data.subarray(currPos, currPos+currLen));
- currPos += currLen;
- }
- // If data remains, buffer it
- if (currPos < data.length) {
- partialBuffer = data.subarray(currPos);
- }
- }
- }
- function send(data) {
- // TODO: if browser accepts views, can optimize this
-#if SOCKET_DEBUG
- Module.print('sender actually sending ' + Array.prototype.slice.call(data));
-#endif
- // ok to use the underlying buffer, we created data and know that the buffer starts at the beginning
- info.socket.send(data.buffer);
- }
- var outQueue = [];
- var intervalling = false, interval;
- function trySend() {
- if (info.socket.readyState != info.socket.OPEN) {
- if (!intervalling) {
- intervalling = true;
- console.log('waiting for socket in order to send');
- interval = setInterval(trySend, 100);
- }
- return;
- }
- for (var i = 0; i < outQueue.length; i++) {
- send(outQueue[i]);
- }
- outQueue.length = 0;
- if (intervalling) {
- intervalling = false;
- clearInterval(interval);
- }
- }
- info.sender = function(data) {
- if (!info.stream) {
- // add a header with the message size
- var header = new Uint8Array(4);
- i32Temp[0] = data.length;
- header.set(i8Temp);
- outQueue.push(header);
- }
- outQueue.push(new Uint8Array(data));
- trySend();
- };
- } catch(e) {
- Module.printErr('Error in connect(): ' + e);
- ___setErrNo(ERRNO_CODES.EACCES);
+ connect__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_read_sockaddr'],
+ connect: function(fd, addrp, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
return -1;
}
- // always "fail" in non-blocking mode
- ___setErrNo(ERRNO_CODES.EINPROGRESS);
- return -1;
+ var info = __read_sockaddr(addrp, addrlen);
+ if (info.errno) {
+ ___setErrNo(info.errno);
+ return -1;
+ }
+ var port = info.port;
+ var addr = DNS.lookup_addr(info.addr) || info.addr;
+
+ try {
+ sock.sock_ops.connect(sock, addr, port);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
},
- recv__deps: ['$FS'],
- recv: function(fd, buf, len, flags) {
- var info = FS.getStream(fd);
- if (!info) {
+ listen__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo'],
+ listen: function(fd, backlog) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
___setErrNo(ERRNO_CODES.EBADF);
return -1;
}
-#if SOCKET_WEBRTC == 0
- if (!info.hasData()) {
- if (info.socket.readyState === WebSocket.CLOSING || info.socket.readyState === WebSocket.CLOSED) {
- // socket has closed
- return 0;
- } else {
- // else, our socket is in a valid state but truly has nothing available
- ___setErrNo(ERRNO_CODES.EAGAIN);
- return -1;
- }
+ try {
+ sock.sock_ops.listen(sock, backlog);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
-#endif
- var buffer = info.inQueue.shift();
-#if SOCKET_DEBUG
- Module.print('recv: ' + [Array.prototype.slice.call(buffer)]);
-#endif
- if (len < buffer.length) {
- if (info.stream) {
- // This is tcp (reliable), so if not all was read, keep it
- info.inQueue.unshift(buffer.subarray(len));
-#if SOCKET_DEBUG
- Module.print('recv: put back: ' + (len - buffer.length));
-#endif
+ },
+
+ accept__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'],
+ accept: function(fd, addrp, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+ try {
+ var newsock = sock.sock_ops.accept(sock);
+ if (addrp) {
+ var res = __write_sockaddr(addr, newsock.family, DNS.lookup_name(newsock.daddr), newsock.dport);
+ assert(!res.errno);
}
- buffer = buffer.subarray(0, len);
+ return newsock.stream.fd;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
- HEAPU8.set(buffer, buf);
- return buffer.length;
},
- send__deps: ['$FS'],
- send: function(fd, buf, len, flags) {
- var info = FS.getStream(fd);
- if (!info) {
+ getsockname__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr', '_inet_pton_raw'],
+ getsockname: function (fd, addr, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
___setErrNo(ERRNO_CODES.EBADF);
return -1;
}
-#if SOCKET_WEBRTC == 0
- if (info.socket.readyState === WebSocket.CLOSING || info.socket.readyState === WebSocket.CLOSED) {
- ___setErrNo(ERRNO_CODES.ENOTCONN);
- return -1;
- } else if (info.socket.readyState === WebSocket.CONNECTING) {
- ___setErrNo(ERRNO_CODES.EAGAIN);
+ try {
+ var info = sock.sock_ops.getname(sock);
+ var res = __write_sockaddr(addr, sock.family, DNS.lookup_name(info.addr), info.port);
+ assert(!res.errno);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
return -1;
}
-#endif
- info.sender(HEAPU8.subarray(buf, buf+len));
- return len;
},
- sendmsg__deps: ['$FS', '$Sockets', 'connect'],
- sendmsg: function(fd, msg, flags) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- // if we are not connected, use the address info in the message
- if (!info.connected) {
- var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
- assert(name, 'sendmsg on non-connected socket, and no name/address in the message');
- _connect(fd, name, {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}});
+ getpeername__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr', '_inet_pton_raw'],
+ getpeername: function (fd, addr, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
- var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
-#if SOCKET_DEBUG
- Module.print('sendmsg vecs: ' + num);
-#endif
- var totalSize = 0;
- for (var i = 0; i < num; i++) {
- totalSize += {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
+ try {
+ var info = sock.sock_ops.getname(sock, true);
+ var res = __write_sockaddr(addr, sock.family, DNS.lookup_name(info.addr), info.port);
+ assert(!res.errno);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
- var buffer = new Uint8Array(totalSize);
- var ret = 0;
- for (var i = 0; i < num; i++) {
- var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
-#if SOCKET_DEBUG
- Module.print('sendmsg curr size: ' + currNum);
-#endif
- if (!currNum) continue;
- var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}};
- buffer.set(HEAPU8.subarray(currBuf, currBuf+currNum), ret);
- ret += currNum;
+ },
+
+ send__deps: ['$SOCKFS', '$ERRNO_CODES', '__setErrNo', 'write'],
+ send: function(fd, buf, len, flags) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- info.sender(buffer); // send all the iovs as a single message
- return ret;
+ // TODO honor flags
+ return _write(fd, buf, len);
},
- recvmsg__deps: ['$FS', '$Sockets', 'connect', 'recv', '__setErrNo', '$ERRNO_CODES', 'htons'],
- recvmsg: function(fd, msg, flags) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- // if we are not connected, use the address info in the message
- if (!info.connected) {
-#if SOCKET_DEBUG
- Module.print('recvmsg connecting');
-#endif
- var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
- assert(name, 'sendmsg on non-connected socket, and no name/address in the message');
- _connect(fd, name, {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}});
+ recv__deps: ['$SOCKFS', '$ERRNO_CODES', '__setErrNo', 'read'],
+ recv: function(fd, buf, len, flags) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- if (!info.hasData()) {
- ___setErrNo(ERRNO_CODES.EWOULDBLOCK);
+ // TODO honor flags
+ return _read(fd, buf, len);
+ },
+
+ sendto__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_read_sockaddr'],
+ sendto: function(fd, message, length, flags, dest_addr, dest_len) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
return -1;
}
- var buffer = info.inQueue.shift();
- var bytes = buffer.length;
-#if SOCKET_DEBUG
- Module.print('recvmsg bytes: ' + bytes);
-#endif
- // write source
- var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
- {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_addr', 'info.addr', 'i32') }}};
- {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_port', '_htons(info.port)', 'i16') }}};
- // write data
- var ret = bytes;
- var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
- var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
- var bufferPos = 0;
- for (var i = 0; i < num && bytes > 0; i++) {
- var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
-#if SOCKET_DEBUG
- Module.print('recvmsg loop ' + [i, num, bytes, currNum]);
-#endif
- if (!currNum) continue;
- currNum = Math.min(currNum, bytes); // XXX what should happen when we partially fill a buffer..?
- bytes -= currNum;
- var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}};
-#if SOCKET_DEBUG
- Module.print('recvmsg call recv ' + currNum);
-#endif
- HEAPU8.set(buffer.subarray(bufferPos, bufferPos + currNum), currBuf);
- bufferPos += currNum;
+
+ // read the address and port to send to
+ var info = __read_sockaddr(dest_addr, dest_len);
+ if (info.errno) {
+ ___setErrNo(info.errno);
+ return -1;
}
- if (info.stream) {
- // This is tcp (reliable), so if not all was read, keep it
- if (bufferPos < bytes) {
- info.inQueue.unshift(buffer.subarray(bufferPos));
-#if SOCKET_DEBUG
- Module.print('recvmsg: put back: ' + (bytes - bufferPos));
-#endif
- }
+ var port = info.port;
+ var addr = DNS.lookup_addr(info.addr) || info.addr;
+
+ // send the message
+ try {
+ var slab = {{{ makeGetSlabs('message', 'i8', true) }}};
+ return sock.sock_ops.sendmsg(sock, slab, message, length, addr, port);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
- return ret;
},
- recvfrom__deps: ['$FS', 'connect', 'recv'],
+ recvfrom__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'],
recvfrom: function(fd, buf, len, flags, addr, addrlen) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- // if we are not connected, use the address info in the message
- if (!info.connected) {
- //var name = {{{ makeGetValue('addr', '0', '*') }}};
- _connect(fd, addr, addrlen);
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- return _recv(fd, buf, len, flags);
- },
- shutdown__deps: ['$FS'],
- shutdown: function(fd, how) {
- var stream = FS.getStream(fd);
- if (!stream) return -1;
- stream.socket.close();
- FS.closeStream(stream);
- },
+ // read from the socket
+ var msg;
+ try {
+ msg = sock.sock_ops.recvmsg(sock, len);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
- ioctl__deps: ['$FS'],
- ioctl: function(fd, request, varargs) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- var bytes = 0;
- if (info.hasData()) {
- bytes = info.inQueue[0].length;
+ if (!msg) {
+ // socket is closed
+ return 0;
}
- var dest = {{{ makeGetValue('varargs', '0', 'i32') }}};
- {{{ makeSetValue('dest', '0', 'bytes', 'i32') }}};
- return 0;
+
+ // write the source address out
+ if (addr) {
+ var res = __write_sockaddr(addr, sock.family, DNS.lookup_name(msg.addr), msg.port);
+ assert(!res.errno);
+ }
+ // write the buffer out
+ HEAPU8.set(msg.buffer, buf);
+
+ return msg.buffer.byteLength;
},
- setsockopt: function(d, level, optname, optval, optlen) {
- console.log('ignoring setsockopt command');
- return 0;
+ sendmsg__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_read_sockaddr'],
+ sendmsg: function(fd, message, flags) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+
+ var iov = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iov', '*') }}};
+ var num = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
+
+ // read the address and port to send to
+ var addr;
+ var port;
+ var name = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_name', '*') }}};
+ var namelen = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}};
+ if (name) {
+ var info = __read_sockaddr(name, namelen);
+ if (info.errno) {
+ ___setErrNo(info.errno);
+ return -1;
+ }
+ port = info.port;
+ addr = DNS.lookup_addr(info.addr) || info.addr;
+ }
+
+ // concatenate scatter-gather arrays into one message buffer
+ var total = 0;
+ for (var i = 0; i < num; i++) {
+ total += {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}};
+ }
+ var view = new Uint8Array(total);
+ var offset = 0;
+ for (var i = 0; i < num; i++) {
+ var iovbase = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_base', 'i8*') }}};
+ var iovlen = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}};
+ for (var j = 0; j < iovlen; j++) {
+ view[offset++] = {{{ makeGetValue('iovbase', 'j', 'i8') }}};
+ }
+ }
+
+ // write the buffer
+ try {
+ return sock.sock_ops.sendmsg(sock, view, 0, total, addr, port);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
},
- bind__deps: ['connect'],
- bind: function(fd, addr, addrlen) {
- _connect(fd, addr, addrlen);
- return 0;
+ recvmsg__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_inet_pton_raw', '_write_sockaddr'],
+ recvmsg: function(fd, message, flags) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+
+ var iov = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
+ var num = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
+
+ // get the total amount of data we can read across all arrays
+ var total = 0;
+ for (var i = 0; i < num; i++) {
+ total += {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}};
+ }
+
+ // try to read total data
+ var msg;
+ try {
+ msg = sock.sock_ops.recvmsg(sock, total);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
+
+ if (!msg) {
+ // socket is closed
+ return 0;
+ }
+
+ // TODO honor flags:
+ // MSG_OOB
+ // Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific.
+ // MSG_PEEK
+ // Peeks at the incoming message.
+ // MSG_WAITALL
+ // Requests that the function block until the full amount of data requested can be returned. The function may return a smaller amount of data if a signal is caught, if the connection is terminated, if MSG_PEEK was specified, or if an error is pending for the socket.
+
+ // write the source address out
+ var name = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_name', '*') }}};
+ if (name) {
+ var res = __write_sockaddr(name, sock.family, DNS.lookup_name(msg.addr), msg.port);
+ assert(!res.errno);
+ }
+ // write the buffer out to the scatter-gather arrays
+ var bytesRead = 0;
+ var bytesRemaining = msg.buffer.byteLength;
+
+ for (var i = 0; bytesRemaining > 0 && i < num; i++) {
+ var iovbase = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_base', 'i8*') }}};
+ var iovlen = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}};
+ if (!iovlen) {
+ continue;
+ }
+ var length = Math.min(iovlen, bytesRemaining);
+ var buf = msg.buffer.subarray(bytesRead, bytesRead + length);
+ HEAPU8.set(buf, iovbase + bytesRead);
+ bytesRead += length;
+ bytesRemaining -= length;
+ }
+
+ // TODO set msghdr.msg_flags
+ // MSG_EOR
+ // End of record was received (if supported by the protocol).
+ // MSG_OOB
+ // Out-of-band data was received.
+ // MSG_TRUNC
+ // Normal data was truncated.
+ // MSG_CTRUNC
+
+ return bytesRead;
},
- listen: function(fd, backlog) {
+ setsockopt: function(fd, level, optname, optval, optlen) {
+ console.log('ignoring setsockopt command');
return 0;
},
- accept__deps: ['$FS', '$Sockets'],
- accept: function(fd, addr, addrlen) {
- // TODO: webrtc queued incoming connections, etc.
- // For now, the model is that bind does a connect, and we "accept" that one connection,
- // which has host:port the same as ours. We also return the same socket fd.
- var info = FS.getStream(fd);
- if (!info) return -1;
- if (addr) {
- setValue(addr + Sockets.sockaddr_in_layout.sin_addr, info.addr, 'i32');
- setValue(addr + Sockets.sockaddr_in_layout.sin_port, info.port, 'i32');
- setValue(addrlen, Sockets.sockaddr_in_layout.__size__, 'i32');
- }
- return fd;
- },
+ // ==========================================================================
+ // select.h
+ // ==========================================================================
- select__deps: ['$FS'],
+ select__deps: ['$FS', '__DEFAULT_POLLMASK'],
select: function(nfds, readfds, writefds, exceptfds, timeout) {
// readfds are supported,
// writefds checks socket open status
// exceptfds not supported
// timeout is always 0 - fully async
- assert(!exceptfds);
+ assert(nfds <= 64, 'nfds must be less than or equal to 64'); // fd sets have 64 bits
+ assert(!exceptfds, 'exceptfds not supported');
- var errorCondition = 0;
-
- function canRead(info) {
- return (info.hasData && info.hasData()) ||
- info.socket.readyState == WebSocket.CLOSING || // let recv return 0 once closed
- info.socket.readyState == WebSocket.CLOSED;
- }
+ var total = 0;
+
+ var srcReadLow = (readfds ? {{{ makeGetValue('readfds', 0, 'i32') }}} : 0),
+ srcReadHigh = (readfds ? {{{ makeGetValue('readfds', 4, 'i32') }}} : 0);
+ var srcWriteLow = (writefds ? {{{ makeGetValue('writefds', 0, 'i32') }}} : 0),
+ srcWriteHigh = (writefds ? {{{ makeGetValue('writefds', 4, 'i32') }}} : 0);
+ var srcExceptLow = (exceptfds ? {{{ makeGetValue('exceptfds', 0, 'i32') }}} : 0),
+ srcExceptHigh = (exceptfds ? {{{ makeGetValue('exceptfds', 4, 'i32') }}} : 0);
+
+ var dstReadLow = 0,
+ dstReadHigh = 0;
+ var dstWriteLow = 0,
+ dstWriteHigh = 0;
+ var dstExceptLow = 0,
+ dstExceptHigh = 0;
+
+ var allLow = (readfds ? {{{ makeGetValue('readfds', 0, 'i32') }}} : 0) |
+ (writefds ? {{{ makeGetValue('writefds', 0, 'i32') }}} : 0) |
+ (exceptfds ? {{{ makeGetValue('exceptfds', 0, 'i32') }}} : 0);
+ var allHigh = (readfds ? {{{ makeGetValue('readfds', 4, 'i32') }}} : 0) |
+ (writefds ? {{{ makeGetValue('writefds', 4, 'i32') }}} : 0) |
+ (exceptfds ? {{{ makeGetValue('exceptfds', 4, 'i32') }}} : 0);
+
+ function get(fd, low, high, val) {
+ return (fd < 32 ? (low & val) : (high & val));
+ }
+
+ for (var fd = 0; fd < nfds; fd++) {
+ var mask = 1 << (fd % 32);
+ if (!(get(fd, allLow, allHigh, mask))) {
+ continue; // index isn't in the set
+ }
- function canWrite(info) {
- return info.socket && (info.socket.readyState == info.socket.OPEN);
- }
+ var stream = FS.getStream(fd);
+ if (!stream) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
- function checkfds(nfds, fds, can) {
- if (!fds) return 0;
+ var flags = ___DEFAULT_POLLMASK;
- var bitsSet = 0;
- var dstLow = 0;
- var dstHigh = 0;
- var srcLow = {{{ makeGetValue('fds', 0, 'i32') }}};
- var srcHigh = {{{ makeGetValue('fds', 4, 'i32') }}};
- nfds = Math.min(64, nfds); // fd sets have 64 bits
+ if (stream.stream_ops.poll) {
+ flags = stream.stream_ops.poll(stream);
+ }
- for (var fd = 0; fd < nfds; fd++) {
- var mask = 1 << (fd % 32), int_ = fd < 32 ? srcLow : srcHigh;
- if (int_ & mask) {
- // index is in the set, check if it is ready for read
- var info = FS.getStream(fd);
- if (!info) {
- ___setErrNo(ERRNO_CODES.EBADF);
- return -1;
- }
- if (can(info)) {
- // set bit
- fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask);
- bitsSet++;
- }
- }
+ if ((flags & {{{ cDefine('POLLIN') }}}) && get(fd, srcReadLow, srcReadHigh, mask)) {
+ fd < 32 ? (dstReadLow = dstReadLow | mask) : (dstReadHigh = dstReadHigh | mask);
+ total++;
+ }
+ if ((flags & {{{ cDefine('POLLOUT') }}}) && get(fd, srcWriteLow, srcWriteHigh, mask)) {
+ fd < 32 ? (dstWriteLow = dstWriteLow | mask) : (dstWriteHigh = dstWriteHigh | mask);
+ total++;
}
+ if ((flags & {{{ cDefine('POLLPRI') }}}) && get(fd, srcExceptLow, srcExceptHigh, mask)) {
+ fd < 32 ? (dstExceptLow = dstExceptLow | mask) : (dstExceptHigh = dstExceptHigh | mask);
+ total++;
+ }
+ }
- {{{ makeSetValue('fds', 0, 'dstLow', 'i32') }}};
- {{{ makeSetValue('fds', 4, 'dstHigh', 'i32') }}};
- return bitsSet;
+ if (readfds) {
+ {{{ makeSetValue('readfds', '0', 'dstReadLow', 'i32') }}};
+ {{{ makeSetValue('readfds', '4', 'dstReadHigh', 'i32') }}};
}
+ if (writefds) {
+ {{{ makeSetValue('writefds', '0', 'dstWriteLow', 'i32') }}};
+ {{{ makeSetValue('writefds', '4', 'dstWriteHigh', 'i32') }}};
+ }
+ if (exceptfds) {
+ {{{ makeSetValue('exceptfds', '0', 'dstExceptLow', 'i32') }}};
+ {{{ makeSetValue('exceptfds', '4', 'dstExceptHigh', 'i32') }}};
+ }
+
+ return total;
+ },
- var totalHandles = checkfds(nfds, readfds, canRead) + checkfds(nfds, writefds, canWrite);
- if (errorCondition) {
+ // ==========================================================================
+ // sys/ioctl.h
+ // ==========================================================================
+
+ ioctl__deps: ['$FS'],
+ ioctl: function(fd, request, varargs) {
+ var stream = FS.getStream(fd);
+ if (!stream) {
___setErrNo(ERRNO_CODES.EBADF);
return -1;
- } else {
- return totalHandles;
}
+ var arg = {{{ makeGetValue('varargs', '0', 'i32') }}};
+ return FS.ioctl(stream, request, arg);
},
#endif
- socketpair__deps: ['__setErrNo', '$ERRNO_CODES'],
- socketpair: function(domain, type, protocol, sv) {
- // int socketpair(int domain, int type, int protocol, int sv[2]);
- // http://pubs.opengroup.org/onlinepubs/009695399/functions/socketpair.html
- ___setErrNo(ERRNO_CODES.EOPNOTSUPP);
- return -1;
- },
-
// pty.h
openpty: function() { throw 'openpty: TODO' },
diff --git a/src/library_browser.js b/src/library_browser.js
index 591a3c11..235ccc78 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -830,15 +830,21 @@ mergeInto(LibraryManager.library, {
},
emscripten_get_now: function() {
- if (ENVIRONMENT_IS_NODE) {
- var t = process['hrtime']();
- return t[0] * 1e3 + t[1] / 1e6;
- }
- else if (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now']) {
- return window['performance']['now']();
- } else {
- return Date.now();
+ if (!_emscripten_get_now.actual) {
+ if (ENVIRONMENT_IS_NODE) {
+ _emscripten_get_now.actual = function() {
+ var t = process['hrtime']();
+ return t[0] * 1e3 + t[1] / 1e6;
+ }
+ } else if (typeof dateNow !== 'undefined') {
+ _emscripten_get_now.actual = dateNow;
+ } else if (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now']) {
+ _emscripten_get_now.actual = function() { return window['performance']['now'](); };
+ } else {
+ _emscripten_get_now.actual = Date.now;
+ }
}
+ return _emscripten_get_now.actual();
},
emscripten_create_worker: function(url) {
diff --git a/src/library_fs.js b/src/library_fs.js
index 1d9748d3..5573dc27 100644
--- a/src/library_fs.js
+++ b/src/library_fs.js
@@ -159,6 +159,9 @@ mergeInto(LibraryManager.library, {
isFIFO: function(mode) {
return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFIFO') }}};
},
+ isSocket: function(mode) {
+ return (mode & {{{ cDefine('S_IFSOCK') }}}) === {{{ cDefine('S_IFSOCK') }}};
+ },
//
// paths
@@ -400,6 +403,9 @@ mergeInto(LibraryManager.library, {
getStream: function(fd) {
return FS.streams[fd];
},
+ // TODO parameterize this function such that a stream
+ // object isn't directly passed in. not possible until
+ // SOCKFS is completed.
createStream: function(stream, fd_start, fd_end) {
var fd = FS.nextfd(fd_start, fd_end);
stream.fd = fd;
@@ -1459,6 +1465,12 @@ mergeInto(LibraryManager.library, {
throw new FS.errnoError(ERRNO_CODES.ENODEV);
}
return stream.stream_ops.mmap(stream, buffer, offset, length, position, prot, flags);
+ },
+ ioctl: function(stream, cmd, arg) {
+ if (!stream.stream_ops.ioctl) {
+ throw new FS.ErrnoError(ERRNO_CODES.ENOTTY);
+ }
+ return stream.stream_ops.ioctl(stream, cmd, arg);
}
}
});
diff --git a/src/library_sdl.js b/src/library_sdl.js
index d6cb6d18..9231f41b 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -2191,11 +2191,37 @@ var LibrarySDL = {
// Joysticks
- SDL_NumJoysticks: function() { return 0 },
+ SDL_NumJoysticks: function() { return 0; },
- SDL_JoystickOpen: function(deviceIndex) { return 0 },
+ SDL_JoystickName: function(deviceIndex) { return 0; },
- SDL_JoystickGetButton: function(joystick, button) { return 0 },
+ SDL_JoystickOpen: function(deviceIndex) { return 0; },
+
+ SDL_JoystickOpened: function(deviceIndex) { return 0; },
+
+ SDL_JoystickIndex: function(joystick) { return 0; },
+
+ SDL_JoystickNumAxes: function(joystick) { return 0; },
+
+ SDL_JoystickNumBalls: function(joystick) { return 0; },
+
+ SDL_JoystickNumHats: function(joystick) { return 0; },
+
+ SDL_JoystickNumButtons: function(joystick) { return 0; },
+
+ SDL_JoystickUpdate: function() {},
+
+ SDL_JoystickEventState: function(state) { return 0; },
+
+ SDL_JoystickGetAxis: function(joystick, axis) { return 0; },
+
+ SDL_JoystickGetHat: function(joystick, hat) { return 0; },
+
+ SDL_JoystickGetBall: function(joystick, ball, dxptr, dyptr) { return -1; },
+
+ SDL_JoystickGetButton: function(joystick, button) { return 0; },
+
+ SDL_JoystickClose: function(joystick) {},
// Misc
diff --git a/src/library_sockfs.js b/src/library_sockfs.js
index 13118b71..b11c6495 100644
--- a/src/library_sockfs.js
+++ b/src/library_sockfs.js
@@ -3,16 +3,574 @@ mergeInto(LibraryManager.library, {
$SOCKFS__deps: ['$FS'],
$SOCKFS: {
mount: function(mount) {
- var node = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0);
- node.node_ops = SOCKFS.node_ops;
- node.stream_ops = SOCKFS.stream_ops;
- return node;
+ return FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0);
},
- node_ops: {
+ nextname: function() {
+ if (!SOCKFS.nextname.current) {
+ SOCKFS.nextname.current = 0;
+ }
+ return 'socket[' + (SOCKFS.nextname.current++) + ']';
},
+ createSocket: function(family, type, protocol) {
+ var streaming = type == {{{ cDefine('SOCK_STREAM') }}};
+ if (protocol) {
+ assert(streaming == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if SOCK_STREAM, must be tcp
+ }
+
+ // create our internal socket structure
+ var sock = {
+ family: family,
+ type: type,
+ protocol: protocol,
+ server: null,
+ peers: {},
+ pending: [],
+ recv_queue: [],
+#if SOCKET_WEBRTC
+#else
+ sock_ops: SOCKFS.websocket_sock_ops
+#endif
+ };
+
+ // create the filesystem node to store the socket structure
+ var name = SOCKFS.nextname();
+ var node = FS.createNode(SOCKFS.root, name, {{{ cDefine('S_IFSOCK') }}}, 0);
+ node.sock = sock;
+
+ // and the wrapping stream that enables library functions such
+ // as read and write to indirectly interact with the socket
+ var stream = FS.createStream({
+ path: name,
+ node: node,
+ flags: FS.modeStringToFlags('r+'),
+ seekable: false,
+ stream_ops: SOCKFS.stream_ops
+ });
+
+ // map the new stream to the socket structure (sockets have a 1:1
+ // relationship with a stream)
+ sock.stream = stream;
+
+ return sock;
+ },
+ getSocket: function(fd) {
+ var stream = FS.getStream(fd);
+ if (!stream || !FS.isSocket(stream.node.mode)) {
+ return null;
+ }
+ return stream.node.sock;
+ },
+ // node and stream ops are backend agnostic
stream_ops: {
+ poll: function(stream) {
+ var sock = stream.node.sock;
+ return sock.sock_ops.poll(sock);
+ },
+ ioctl: function(stream, request, varargs) {
+ var sock = stream.node.sock;
+ return sock.sock_ops.ioctl(sock, request, varargs);
+ },
+ read: function(stream, buffer, offset, length, position /* ignored */) {
+ var sock = stream.node.sock;
+ var msg = sock.sock_ops.recvmsg(sock, length);
+ if (!msg) {
+ // socket is closed
+ return 0;
+ }
+#if USE_TYPED_ARRAYS == 2
+ buffer.set(msg.buffer, offset);
+#else
+ for (var i = 0; i < size; i++) {
+ buffer[offset + i] = msg.buffer[i];
+ }
+#endif
+ return msg.buffer.length;
+ },
+ write: function(stream, buffer, offset, length, position /* ignored */) {
+ var sock = stream.node.sock;
+ return sock.sock_ops.sendmsg(sock, buffer, offset, length);
+ },
+ close: function(stream) {
+ var sock = stream.node.sock;
+ sock.sock_ops.close(sock);
+ }
},
+ // backend-specific stream ops
websocket_sock_ops: {
+ //
+ // peers are a small wrapper around a WebSocket to help in
+ // emulating dgram sockets
+ //
+ // these functions aren't actually sock_ops members, but we're
+ // abusing the namespace to organize them
+ //
+ createPeer: function(sock, addr, port) {
+ var ws;
+
+ if (typeof addr === 'object') {
+ ws = addr;
+ addr = null;
+ port = null;
+ }
+
+ if (ws) {
+ // for sockets that've already connected (e.g. we're the server)
+ // we can inspect the _socket property for the address
+ if (ws._socket) {
+ addr = ws._socket.remoteAddress;
+ port = ws._socket.remotePort;
+ }
+ // if we're just now initializing a connection to the remote,
+ // inspect the url property
+ else {
+ var result = /ws[s]?:\/\/([^:]+):(\d+)/.exec(ws.url);
+ if (!result) {
+ throw new Error('WebSocket URL must be in the format ws(s)://address:port');
+ }
+ addr = result[1];
+ port = parseInt(result[2], 10);
+ }
+ } else {
+ // create the actual websocket object and connect
+ try {
+ var url = 'ws://' + addr + ':' + port;
+#if SOCKET_DEBUG
+ console.log('connect: ' + url);
+#endif
+ // the node ws library API is slightly different than the browser's
+ var opts = ENVIRONMENT_IS_NODE ? {} : ['binary'];
+ ws = new WebSocket(url, opts);
+ ws.binaryType = 'arraybuffer';
+ } catch (e) {
+ throw new FS.ErrnoError(ERRNO_CODES.EHOSTUNREACH);
+ }
+ }
+
+#if SOCKET_DEBUG
+ Module.print('websocket adding peer: ' + addr + ':' + port);
+#endif
+
+ var peer = {
+ addr: addr,
+ port: port,
+ socket: ws,
+ dgram_send_queue: []
+ };
+
+ SOCKFS.websocket_sock_ops.addPeer(sock, peer);
+ SOCKFS.websocket_sock_ops.handlePeerEvents(sock, peer);
+
+ // if this is a bound dgram socket, send the port number first to allow
+ // us to override the ephemeral port reported to us by remotePort on the
+ // remote end.
+ if (sock.type === {{{ cDefine('SOCK_DGRAM') }}} && typeof sock.sport !== 'undefined') {
+#if SOCKET_DEBUG
+ Module.print('websocket queuing port message (port ' + sock.sport + ')');
+#endif
+ peer.dgram_send_queue.push(new Uint8Array([
+ 255, 255, 255, 255,
+ 'p'.charCodeAt(0), 'o'.charCodeAt(0), 'r'.charCodeAt(0), 't'.charCodeAt(0),
+ ((sock.sport & 0xff00) >> 8) , (sock.sport & 0xff)
+ ]));
+ }
+
+ return peer;
+ },
+ getPeer: function(sock, addr, port) {
+ return sock.peers[addr + ':' + port];
+ },
+ addPeer: function(sock, peer) {
+ sock.peers[peer.addr + ':' + peer.port] = peer;
+ },
+ removePeer: function(sock, peer) {
+ delete sock.peers[peer.addr + ':' + peer.port];
+ },
+ handlePeerEvents: function(sock, peer) {
+ var first = true;
+
+ var handleOpen = function () {
+#if SOCKET_DEBUG
+ Module.print('websocket handle open');
+#endif
+ try {
+ var queued = peer.dgram_send_queue.shift();
+ while (queued) {
+#if SOCKET_DEBUG
+ Module.print('websocket sending queued data (' + queued.byteLength + ' bytes): ' + [Array.prototype.slice.call(new Uint8Array(queued))]);
+#endif
+ peer.socket.send(queued);
+ queued = peer.dgram_send_queue.shift();
+ }
+ } catch (e) {
+ // not much we can do here in the way of proper error handling as we've already
+ // lied and said this data was sent. shut it down.
+ peer.socket.close();
+ }
+ };
+
+ var handleMessage = function(data) {
+ assert(typeof data !== 'string' && data.byteLength !== undefined); // must receive an ArrayBuffer
+ data = new Uint8Array(data); // make a typed array view on the array buffer
+
+#if SOCKET_DEBUG
+ Module.print('websocket handle message (' + data.byteLength + ' bytes): ' + [Array.prototype.slice.call(data)]);
+#endif
+
+ // if this is the port message, override the peer's port with it
+ var wasfirst = first;
+ first = false;
+ if (wasfirst &&
+ data.length === 10 &&
+ data[0] === 255 && data[1] === 255 && data[2] === 255 && data[3] === 255 &&
+ data[4] === 'p'.charCodeAt(0) && data[5] === 'o'.charCodeAt(0) && data[6] === 'r'.charCodeAt(0) && data[7] === 't'.charCodeAt(0)) {
+ // update the peer's port and it's key in the peer map
+ var newport = ((data[8] << 8) | data[9]);
+ SOCKFS.websocket_sock_ops.removePeer(sock, peer);
+ peer.port = newport;
+ SOCKFS.websocket_sock_ops.addPeer(sock, peer);
+ return;
+ }
+
+ sock.recv_queue.push({ addr: peer.addr, port: peer.port, data: data });
+ };
+
+ if (ENVIRONMENT_IS_NODE) {
+ peer.socket.on('open', handleOpen);
+ peer.socket.on('message', function(data, flags) {
+ if (!flags.binary) {
+ return;
+ }
+ handleMessage((new Uint8Array(data)).buffer); // copy from node Buffer -> ArrayBuffer
+ });
+ peer.socket.on('error', function() {
+ // don't throw
+ });
+ } else {
+ peer.socket.onopen = handleOpen;
+ peer.socket.onmessage = function(event) {
+ handleMessage(event.data);
+ };
+ }
+ },
+
+ //
+ // actual sock ops
+ //
+ poll: function(sock) {
+ if (sock.type === {{{ cDefine('SOCK_STREAM') }}} && sock.server) {
+ // listen sockets should only say they're available for reading
+ // if there are pending clients.
+ return sock.pending.length ? ({{{ cDefine('POLLRDNORM') }}} | {{{ cDefine('POLLIN') }}}) : 0;
+ }
+
+ var mask = 0;
+ var dest = sock.type === {{{ cDefine('SOCK_STREAM') }}} ? // we only care about the socket state for connection-based sockets
+ SOCKFS.websocket_sock_ops.getPeer(sock, sock.daddr, sock.dport) :
+ null;
+
+ if (sock.recv_queue.length ||
+ !dest || // connection-less sockets are always ready to read
+ (dest && dest.socket.readyState === dest.socket.CLOSING) ||
+ (dest && dest.socket.readyState === dest.socket.CLOSED)) { // let recv return 0 once closed
+ mask |= ({{{ cDefine('POLLRDNORM') }}} | {{{ cDefine('POLLIN') }}});
+ }
+
+ if (!dest || // connection-less sockets are always ready to write
+ (dest && dest.socket.readyState === dest.socket.OPEN)) {
+ mask |= {{{ cDefine('POLLOUT') }}};
+ }
+
+ if ((dest && dest.socket.readyState === dest.socket.CLOSING) ||
+ (dest && dest.socket.readyState === dest.socket.CLOSED)) {
+ mask |= {{{ cDefine('POLLHUP') }}};
+ }
+
+ return mask;
+ },
+ ioctl: function(sock, request, arg) {
+ switch (request) {
+ case {{{ cDefine('FIONREAD') }}}:
+ var bytes = 0;
+ if (sock.recv_queue.length) {
+ bytes = sock.recv_queue[0].data.length;
+ }
+ {{{ makeSetValue('arg', '0', 'bytes', 'i32') }}};
+ return 0;
+ default:
+ return ERRNO_CODES.EINVAL;
+ }
+ },
+ close: function(sock) {
+ // if we've spawned a listen server, close it
+ if (sock.server) {
+ try {
+ sock.server.close();
+ } catch (e) {
+ }
+ sock.server = null;
+ }
+ // close any peer connections
+ var peers = Object.keys(sock.peers);
+ for (var i = 0; i < peers.length; i++) {
+ var peer = sock.peers[peers[i]];
+ try {
+ peer.socket.close();
+ } catch (e) {
+ }
+ SOCKFS.websocket_sock_ops.removePeer(sock, peer);
+ }
+ return 0;
+ },
+ bind: function(sock, addr, port) {
+ if (typeof sock.saddr !== 'undefined' || typeof sock.sport !== 'undefined') {
+ throw new FS.ErrnoError(ERRNO_CODES.EINVAL); // already bound
+ }
+ sock.saddr = addr;
+ sock.sport = port || _mkport();
+ // in order to emulate dgram sockets, we need to launch a listen server when
+ // binding on a connection-less socket
+ // note: this is only required on the server side
+ if (sock.type === {{{ cDefine('SOCK_DGRAM') }}}) {
+ // close the existing server if it exists
+ if (sock.server) {
+ sock.server.close();
+ sock.server = null;
+ }
+ // swallow error operation not supported error that occurs when binding in the
+ // browser where this isn't supported
+ try {
+ sock.sock_ops.listen(sock, 0);
+ } catch (e) {
+ if (!(e instanceof FS.ErrnoError)) throw e;
+ if (e.errno !== ERRNO_CODES.EOPNOTSUPP) throw e;
+ }
+ }
+ },
+ connect: function(sock, addr, port) {
+ if (sock.server) {
+ throw new FS.ErrnoError(ERRNO_CODS.EOPNOTSUPP);
+ }
+
+ // TODO autobind
+ // if (!sock.addr && sock.type == {{{ cDefine('SOCK_DGRAM') }}}) {
+ // }
+
+ // early out if we're already connected / in the middle of connecting
+ if (typeof sock.daddr !== 'undefined' && typeof sock.dport !== 'undefined') {
+ var dest = SOCKFS.websocket_sock_ops.getPeer(sock, sock.daddr, sock.dport);
+ if (dest) {
+ if (dest.socket.readyState === dest.socket.CONNECTING) {
+ throw new FS.ErrnoError(ERRNO_CODES.EALREADY);
+ } else {
+ throw new FS.ErrnoError(ERRNO_CODES.EISCONN);
+ }
+ }
+ }
+
+ // add the socket to our peer list and set our
+ // destination address / port to match
+ var peer = SOCKFS.websocket_sock_ops.createPeer(sock, addr, port);
+ sock.daddr = peer.addr;
+ sock.dport = peer.port;
+
+ // always "fail" in non-blocking mode
+ throw new FS.ErrnoError(ERRNO_CODES.EINPROGRESS);
+ },
+ listen: function(sock, backlog) {
+ if (!ENVIRONMENT_IS_NODE) {
+ throw new FS.ErrnoError(ERRNO_CODES.EOPNOTSUPP);
+ }
+ if (sock.server) {
+ throw new FS.ErrnoError(ERRNO_CODES.EINVAL); // already listening
+ }
+ var WebSocketServer = require('ws').Server;
+ var host = sock.saddr;
+#if SOCKET_DEBUG
+ console.log('listen: ' + host + ':' + sock.sport);
+#endif
+ sock.server = new WebSocketServer({
+ host: host,
+ port: sock.sport
+ // TODO support backlog
+ });
+
+ sock.server.on('connection', function(ws) {
+#if SOCKET_DEBUG
+ console.log('received connection from: ' + ws._socket.remoteAddress + ':' + ws._socket.remotePort);
+#endif
+ if (sock.type === {{{ cDefine('SOCK_STREAM') }}}) {
+ var newsock = SOCKFS.createSocket(sock.family, sock.type, sock.protocol);
+
+ // create a peer on the new socket
+ var peer = SOCKFS.websocket_sock_ops.createPeer(newsock, ws);
+ newsock.daddr = peer.addr;
+ newsock.dport = peer.port;
+
+ // push to queue for accept to pick up
+ sock.pending.push(newsock);
+ } else {
+ // create a peer on the listen socket so calling sendto
+ // with the listen socket and an address will resolve
+ // to the correct client
+ SOCKFS.websocket_sock_ops.createPeer(sock, ws);
+ }
+ });
+ sock.server.on('closed', function() {
+ sock.server = null;
+ });
+ sock.server.on('error', function() {
+ // don't throw
+ });
+ },
+ accept: function(listensock) {
+ if (!listensock.server) {
+ throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
+ }
+ var newsock = listensock.pending.shift();
+ newsock.stream.flags = listensock.stream.flags;
+ return newsock;
+ },
+ getname: function(sock, peer) {
+ var addr, port;
+ if (peer) {
+ if (sock.daddr === undefined || sock.dport === undefined) {
+ throw new FS.ErrnoError(ERRNO_CODES.ENOTCONN);
+ }
+ addr = sock.daddr;
+ port = sock.dport;
+ } else {
+ // TODO saddr and sport will be set for bind()'d UDP sockets, but what
+ // should we be returning for TCP sockets that've been connect()'d?
+ addr = sock.saddr || 0;
+ port = sock.sport || 0;
+ }
+ return { addr: addr, port: port };
+ },
+ sendmsg: function(sock, buffer, offset, length, addr, port) {
+ if (sock.type === {{{ cDefine('SOCK_DGRAM') }}}) {
+ // connection-less sockets will honor the message address,
+ // and otherwise fall back to the bound destination address
+ if (addr === undefined || port === undefined) {
+ addr = sock.daddr;
+ port = sock.dport;
+ }
+ // if there was no address to fall back to, error out
+ if (addr === undefined || port === undefined) {
+ throw new FS.ErrnoError(ERRNO_CODES.EDESTADDRREQ);
+ }
+ } else {
+ // connection-based sockets will only use the bound
+ addr = sock.daddr;
+ port = sock.dport;
+ }
+
+ // find the peer for the destination address
+ var dest = SOCKFS.websocket_sock_ops.getPeer(sock, addr, port);
+
+ // early out if not connected with a connection-based socket
+ if (sock.type === {{{ cDefine('SOCK_STREAM') }}}) {
+ if (!dest || dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) {
+ throw new FS.ErrnoError(ERRNO_CODES.ENOTCONN);
+ } else if (dest.socket.readyState === dest.socket.CONNECTING) {
+ throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
+ }
+ }
+
+ // create a copy of the incoming data to send, as the WebSocket API
+ // doesn't work entirely with an ArrayBufferView, it'll just send
+ // the entire underlying buffer
+ var data;
+ if (buffer instanceof Array || buffer instanceof ArrayBuffer) {
+ data = buffer.slice(offset, offset + length);
+ } else { // ArrayBufferView
+ data = buffer.buffer.slice(buffer.byteOffset + offset, buffer.byteOffset + offset + length);
+ }
+
+ // if we're emulating a connection-less dgram socket and don't have
+ // a cached connection, queue the buffer to send upon connect and
+ // lie, saying the data was sent now.
+ if (sock.type === {{{ cDefine('SOCK_DGRAM') }}}) {
+ if (!dest || dest.socket.readyState !== dest.socket.OPEN) {
+ // if we're not connected, open a new connection
+ if (!dest || dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) {
+ dest = SOCKFS.websocket_sock_ops.createPeer(sock, addr, port);
+ }
+#if SOCKET_DEBUG
+ Module.print('websocket queuing (' + length + ' bytes): ' + [Array.prototype.slice.call(new Uint8Array(data))]);
+#endif
+ dest.dgram_send_queue.push(data);
+ return length;
+ }
+ }
+
+ try {
+#if SOCKET_DEBUG
+ Module.print('websocket send (' + length + ' bytes): ' + [Array.prototype.slice.call(new Uint8Array(data))]);
+#endif
+ // send the actual data
+ dest.socket.send(data);
+ return length;
+ } catch (e) {
+ throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
+ }
+ },
+ recvmsg: function(sock, length) {
+ // http://pubs.opengroup.org/onlinepubs/7908799/xns/recvmsg.html
+ if (sock.type === {{{ cDefine('SOCK_STREAM') }}} && sock.server) {
+ // tcp servers should not be recv()'ing on the listen socket
+ throw new FS.ErrnoError(ERRNO_CODES.ENOTCONN);
+ }
+
+ var queued = sock.recv_queue.shift();
+ if (!queued) {
+ if (sock.type === {{{ cDefine('SOCK_STREAM') }}}) {
+ var dest = SOCKFS.websocket_sock_ops.getPeer(sock, sock.daddr, sock.dport);
+
+ if (!dest) {
+ // if we have a destination address but are not connected, error out
+ throw new FS.ErrnoError(ERRNO_CODES.ENOTCONN);
+ }
+ else if (dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) {
+ // return null if the socket has closed
+ return null;
+ }
+ else {
+ // else, our socket is in a valid state but truly has nothing available
+ throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
+ }
+ } else {
+ throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
+ }
+ }
+
+ // queued.data will be an ArrayBuffer if it's unadulterated, but if it's
+ // requeued TCP data it'll be an ArrayBufferView
+ var queuedLength = queued.data.byteLength || queued.data.length;
+ var queuedOffset = queued.data.byteOffset || 0;
+ var queuedBuffer = queued.data.buffer || queued.data;
+ var bytesRead = Math.min(length, queuedLength);
+ var res = {
+ buffer: new Uint8Array(queuedBuffer, queuedOffset, bytesRead),
+ addr: queued.addr,
+ port: queued.port
+ };
+
+#if SOCKET_DEBUG
+ Module.print('websocket read (' + bytesRead + ' bytes): ' + [Array.prototype.slice.call(res.buffer)]);
+#endif
+
+ // push back any unread data for TCP connections
+ if (sock.type === {{{ cDefine('SOCK_STREAM') }}} && bytesRead < queuedLength) {
+ var bytesRemaining = queuedLength - bytesRead;
+#if SOCKET_DEBUG
+ Module.print('websocket read: put back ' + bytesRemaining + ' bytes');
+#endif
+ queued.data = new Uint8Array(queuedBuffer, queuedOffset + bytesRead, bytesRemaining);
+ sock.recv_queue.unshift(queued);
+ }
+
+ return res;
+ }
}
}
}); \ No newline at end of file
diff --git a/src/library_tty.js b/src/library_tty.js
index 8f44cd07..bf4a2472 100644
--- a/src/library_tty.js
+++ b/src/library_tty.js
@@ -1,17 +1,37 @@
mergeInto(LibraryManager.library, {
$TTY__deps: ['$FS'],
+ $TTY__postset: '__ATINIT__.unshift({ func: function() { TTY.init() } });' +
+ '__ATEXIT__.push({ func: function() { TTY.shutdown() } });' +
+ 'TTY.utf8 = new Runtime.UTF8Processor();',
$TTY: {
ttys: [],
+ init: function () {
+ // https://github.com/kripken/emscripten/pull/1555
+ // if (ENVIRONMENT_IS_NODE) {
+ // // currently, FS.init does not distinguish if process.stdin is a file or TTY
+ // // device, it always assumes it's a TTY device. because of this, we're forcing
+ // // process.stdin to UTF8 encoding to at least make stdin reading compatible
+ // // with text files until FS.init can be refactored.
+ // process['stdin']['setEncoding']('utf8');
+ // }
+ },
+ shutdown: function() {
+ // https://github.com/kripken/emscripten/pull/1555
+ // if (ENVIRONMENT_IS_NODE) {
+ // // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)?
+ // // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation
+ // // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists?
+ // // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle
+ // // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call
+ // process['stdin']['pause']();
+ // }
+ },
register: function(dev, ops) {
TTY.ttys[dev] = { input: [], output: [], ops: ops };
FS.registerDevice(dev, TTY.stream_ops);
},
stream_ops: {
open: function(stream) {
- // this wouldn't be required if the library wasn't eval'd at first...
- if (!TTY.utf8) {
- TTY.utf8 = new Runtime.UTF8Processor();
- }
var tty = TTY.ttys[stream.node.rdev];
if (!tty) {
throw new FS.ErrnoError(ERRNO_CODES.ENODEV);
@@ -66,17 +86,22 @@ mergeInto(LibraryManager.library, {
return i;
}
},
- // NOTE: This is weird to support stdout and stderr
- // overrides in addition to print and printErr overrides.
default_tty_ops: {
+ // get_char has 3 particular return values:
+ // a.) the next character represented as an integer
+ // b.) undefined to signal that no data is currently available
+ // c.) null to signal an EOF
get_char: function(tty) {
if (!tty.input.length) {
var result = null;
if (ENVIRONMENT_IS_NODE) {
- if (process.stdin.destroyed) {
- return undefined;
+ result = process['stdin']['read']();
+ if (!result) {
+ if (process['stdin']['_readableState'] && process['stdin']['_readableState']['ended']) {
+ return null; // EOF
+ }
+ return undefined; // no data available
}
- result = process.stdin.read();
} else if (typeof window != 'undefined' &&
typeof window.prompt == 'function') {
// Browser.
diff --git a/src/modules.js b/src/modules.js
index fa6c0983..373e60d9 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -443,6 +443,7 @@ var LibraryManager = {
},
isStubFunction: function(ident) {
+ if (SIDE_MODULE == 1) return false; // cannot eliminate these, as may be implement in the main module and imported by us
var libCall = LibraryManager.library[ident.substr(1)];
return typeof libCall === 'function' && libCall.toString().replace(/\s/g, '') === 'function(){}'
&& !(ident in Functions.implementedFunctions);
diff --git a/src/settings.js b/src/settings.js
index 03b4ed64..79c54c2b 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -354,9 +354,10 @@ var LINKABLE = 0; // If set to 1, this file can be linked with others, either as
// LINKABLE of 0 is very useful in that we can reduce the size of the
// generated code very significantly, by removing everything not actually used.
-var DLOPEN_SUPPORT = 0; // Whether to support dlopen(NULL, ...) which enables dynamic access to the
- // module's functions and globals. Implies LINKABLE=1, because we do not want
- // dead code elimination.
+var DLOPEN_SUPPORT = 0; // Full support for dlopen. This is necessary for asm.js and for all code
+ // modes for dlopen(NULL, ...). Note that you must use EMSCRIPTEN_KEEPALIVE
+ // to ensure that functions and globals can be accessed through dlsym,
+ // otherwise LLVM may optimize them out.
var RUNTIME_TYPE_INFO = 0; // Whether to expose type info to the script at run time. This
// increases the size of the generated script, but allows you
@@ -457,6 +458,7 @@ var C_DEFINES = {
'ABMON_8': '40',
'ABMON_9': '41',
'ACCESSPERMS': '0000400',
+ 'AF_UNSPEC': '0',
'AF_INET': '2',
'AF_INET6': '10',
'ALLPERMS': '0004000',
@@ -718,6 +720,7 @@ var C_DEFINES = {
'PM_STR': '6',
'POLLERR': '8',
'POLLHUP': '16',
+ 'POLLPRI': '32',
'POLLIN': '1',
'POLLNVAL': '4',
'POLLOUT': '2',
@@ -874,9 +877,9 @@ var C_DEFINES = {
'SOCK_DGRAM': '2',
'SOCK_STREAM': '1',
'STDC_HEADERS': '1',
- 'STDERR_FILENO': '2',
- 'STDIN_FILENO': '0',
- 'STDOUT_FILENO': '1',
+ 'STDERR_FILENO': '3',
+ 'STDIN_FILENO': '1',
+ 'STDOUT_FILENO': '2',
'S_BLKSIZE': '1024',
'S_ENFMT': '0002000',
'S_IEXEC': '0000100',
@@ -1457,6 +1460,35 @@ var C_DEFINES = {
'ECANCELED': '140',
'ENOTRECOVERABLE': '141',
'EOWNERDEAD': '142',
- 'ESTRPIPE': '143'
+ 'ESTRPIPE': '143',
+ 'AI_PASSIVE': '0x0001',
+ 'AI_CANONNAME': '0x0002',
+ 'AI_NUMERICHOST': '0x0004',
+ 'AI_V4MAPPED': '0x0008',
+ 'AI_ALL': '0x0010',
+ 'AI_ADDRCONFIG': '0x0020',
+ 'AI_NUMERICSERV': '0x0400',
+ 'EAI_ADDRFAMILY': '1',
+ 'EAI_AGAIN': '2',
+ 'EAI_BADFLAGS': '3',
+ 'EAI_FAIL': '4',
+ 'EAI_FAMILY': '5',
+ 'EAI_MEMORY': '6',
+ 'EAI_NODATA': '7',
+ 'EAI_NONAME': '8',
+ 'EAI_SERVICE': '9',
+ 'EAI_SOCKTYPE': '10',
+ 'EAI_SYSTEM': '11',
+ 'EAI_BADHINTS': '12',
+ 'EAI_PROTOCOL': '13',
+ 'EAI_OVERFLOW': '14',
+ 'EAI_MAX': '15',
+ 'NI_NOFQDN': '0x00000001',
+ 'NI_NUMERICHOST': '0x00000002',
+ 'NI_NAMEREQD': '0x00000004',
+ 'NI_NUMERICSERV': '0x00000008',
+ 'NI_DGRAM': '0x00000010',
+ 'INADDR_ANY': '0',
+ 'INADDR_LOOPBACK': '0x7f000001'
};
diff --git a/src/shell_sharedlib.js b/src/shell_sharedlib.js
index 1d34c73e..c070c617 100644
--- a/src/shell_sharedlib.js
+++ b/src/shell_sharedlib.js
@@ -1,8 +1,24 @@
// Capture the output of this into a variable, if you want
-(function(FUNCTION_TABLE_OFFSET, globalScope) {
+(function(FUNCTION_TABLE_OFFSET, parentModule) {
var Module = {};
var args = [];
Module.arguments = [];
+ Module.print = parentModule.print;
+ Module.printErr = parentModule.printErr;
+
+ Module.cleanups = [];
+
+#if ASM_JS
+ var H_BASE = 0;
+ // Each module has its own stack
+ var STACKTOP = parentModule['_malloc'](TOTAL_STACK);
+ assert(STACKTOP % 8 == 0);
+ var STACK_MAX = STACKTOP + TOTAL_STACK;
+ Module.cleanups.push(function() {
+ parentModule['_free'](STACKTOP); // XXX ensure exported
+ parentModule['_free'](H_BASE);
+ });
+#endif
{{BODY}}
@@ -10,3 +26,4 @@
return Module;
});
+
diff --git a/system/include/netdb.h b/system/include/netdb.h
index df74a117..8860a14a 100644
--- a/system/include/netdb.h
+++ b/system/include/netdb.h
@@ -101,6 +101,7 @@ struct hostent
struct hostent* gethostbyaddr(const void* addr, socklen_t len, int type);
struct hostent* gethostbyname(const char* name);
+struct hostent* gethostbyname_r(const char *name, struct hostent *ret, char *buf, int buflen, int *err); // XXX not quite standard, see http://linux.die.net/man/3/gethostbyname_r
void sethostent(int stayopen);
void endhostent(void);
void herror(const char* s);
diff --git a/tests/browser_main.cpp b/tests/browser_main.cpp
new file mode 100644
index 00000000..efdce1be
--- /dev/null
+++ b/tests/browser_main.cpp
@@ -0,0 +1,42 @@
+#include <assert.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <emscripten.h>
+
+typedef void (*voidfunc)();
+typedef int (*intfunc)();
+
+void *lib_handle;
+voidfunc onefunc;
+intfunc twofunc;
+
+void next(const char *x) {
+ lib_handle = dlopen("themodule.js", RTLD_NOW);
+ assert(lib_handle != NULL);
+
+ onefunc = (voidfunc)dlsym(lib_handle, "one");
+ twofunc = (intfunc)dlsym(lib_handle, "two");
+ assert(onefunc && twofunc);
+
+ assert(twofunc() == 0);
+ onefunc();
+ assert(twofunc() == 1);
+ onefunc();
+ onefunc();
+ assert(twofunc() == 3);
+ onefunc();
+ onefunc();
+ onefunc();
+ onefunc();
+ assert(twofunc() == 7);
+ onefunc();
+ int result = twofunc();
+ REPORT_RESULT();
+}
+
+int main() {
+ emscripten_async_wget("module.js", "themodule.js", next, NULL);
+
+ return 0;
+}
+
diff --git a/tests/browser_module.cpp b/tests/browser_module.cpp
new file mode 100644
index 00000000..85d724b5
--- /dev/null
+++ b/tests/browser_module.cpp
@@ -0,0 +1,15 @@
+
+int state = 0;
+
+extern "C" {
+
+void one() {
+ state++;
+}
+
+int two() {
+ return state;
+}
+
+}
+
diff --git a/tests/dlmalloc_proxy.c b/tests/dlmalloc_proxy.c
new file mode 100644
index 00000000..06137c42
--- /dev/null
+++ b/tests/dlmalloc_proxy.c
@@ -0,0 +1,85 @@
+// Emscripten tests
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <dlfcn.h>
+
+typedef void *(*mallocer)(int n);
+typedef void (*freeer)(void *p);
+
+void *lib_handle;
+int handles = 0;
+mallocer mallocproxy = NULL;
+freeer freeproxy = NULL;
+
+void get_lib() {
+ //printf("get lib\n");
+ lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle != NULL);
+ handles++;
+
+ mallocproxy = (mallocer)dlsym(lib_handle, "mallocproxy");
+ assert(mallocproxy!= NULL);
+ freeproxy = (freeer)dlsym(lib_handle, "freeproxy");
+ assert(freeproxy!= NULL);
+}
+
+void unget_lib() {
+ //printf("unget lib\n");
+ assert(lib_handle);
+ dlclose(lib_handle);
+ handles--;
+ if (handles == 0) lib_handle = NULL;
+}
+
+int main() {
+ int n = 0, total = 0, l = 0;
+ void *allocs[50];
+ allocs[10] = malloc(10); // pull in real malloc
+ for (int i = 0; i < 1000; i++) {
+ //printf("%d: total ever %d MB, current MB %d, total libs %d\n", i, total, n, l);
+ if (i % 5 == 0) {
+ if (handles < 10) {
+ get_lib();
+ l++;
+ }
+ }
+ if (i % 7 == 0) {
+ if (handles > 0) unget_lib();
+ }
+ if (i % 3 == 0) {
+ if (handles > 0) {
+ if (n < 10) {
+ if (i % 2 == 0) {
+ //printf("alloc\n");
+ allocs[n++] = mallocproxy(1024*1024);
+ } else {
+ //printf("real alloc\n");
+ allocs[n++] = malloc(1024*1024);
+ }
+ total++;
+ } else {
+ //printf("real free\n");
+ free(allocs[--n]); // real free
+ }
+ }
+ }
+ if (i % 4 == 0) {
+ if (handles > 0 && n > 0) {
+ //printf("free\n");
+ if (i % 2 == 0) {
+ //printf("free\n");
+ freeproxy(allocs[--n]);
+ } else {
+ //printf("real free\n");
+ free(allocs[--n]);
+ }
+ }
+ }
+ }
+ while (n > 0) free(allocs[--n]); // real free
+ while (handles > 0) unget_lib();
+ printf("*%d,%d*\n", total, l);
+}
+
diff --git a/tests/emscripten_get_now.cpp b/tests/emscripten_get_now.cpp
new file mode 100644
index 00000000..17aa7d32
--- /dev/null
+++ b/tests/emscripten_get_now.cpp
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include "emscripten.h"
+
+#ifndef REPORT_RESULT
+// To be able to run this test outside the browser harness in node.js/spidermonkey:
+#define REPORT_RESULT int dummy
+#endif
+
+int result = 0;
+
+int main() {
+ // This code tests three things:
+ // a) Calling emscripten_get_now(), time actually proceeds.
+ // b) Values returned by emscripten_get_now() are strictly nondecreasing.
+ // c) emscripten_get_now() is able to return sub-millisecond precision timer values.
+ bool detected_good_timer_precision = false;
+ float smallest_delta = 0.f;
+ for(int x = 0; x < 1000; ++x) { // Have several attempts to find a good small delta, i.e. give time to JS engine to warm up the code and so on.
+ float t = emscripten_get_now();
+ float t2 = emscripten_get_now();
+ for(int i = 0; i < 100 && t == t2; ++i) {
+ t2 = emscripten_get_now();
+ }
+
+ if (t2 < t && t2 - t < 1000.f) { // Timer must be monotonous.
+ printf("Timer is not monotonous!\\n");
+ smallest_delta = t2 - t;
+ break;
+ }
+ if (t2 > t && t2 - t < 0.7f) { // Must pass less than a millisecond between two calls.
+ detected_good_timer_precision = true;
+ smallest_delta = t2 - t;
+ break;
+ }
+ }
+
+ if (detected_good_timer_precision) {
+ printf("Timer resolution is good. (%f msecs)\\n", smallest_delta);
+ result = 1;
+ } else {
+ printf("Error: Bad timer precision: Smallest timer delta: %f msecs\\n", smallest_delta);
+ result = 0;
+ }
+ REPORT_RESULT();
+ return 0;
+}
diff --git a/tests/module/test_stdin.c b/tests/module/test_stdin.c
new file mode 100644
index 00000000..4838d466
--- /dev/null
+++ b/tests/module/test_stdin.c
@@ -0,0 +1,57 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#if EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+int line = 0;
+
+void main_loop(void *arg)
+{
+ char str[10] = {0};
+ int ret;
+
+ errno = 0;
+ while (errno != EAGAIN) {
+ if (line == 0) {
+ ret = fgetc(stdin);
+ if (ret != EOF) putc(ret, stdout);
+ if (ret == '\n') line++;
+ } else if (line > 0) {
+ ret = scanf("%10s", str);
+ if (ret > 0) puts(str);
+ }
+
+ if (ferror(stdin) && errno != EAGAIN) {
+ puts("error");
+ exit(EXIT_FAILURE);
+ }
+
+ if (feof(stdin)) {
+ puts("eof");
+ exit(EXIT_SUCCESS);
+ }
+
+ clearerr(stdin);
+ }
+}
+
+int main(int argc, char const *argv[])
+{
+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
+
+ // SM shell doesn't implement an event loop and therefor doesn't support
+ // emscripten_set_main_loop. However, its stdin reads are sync so it
+ // should exit out after calling main_loop once.
+ main_loop(NULL);
+
+#if EMSCRIPTEN
+ emscripten_set_main_loop(main_loop, 60, 0);
+#else
+ while (1) main_loop(NULL); sleep(1);
+#endif
+ return 0;
+} \ No newline at end of file
diff --git a/tests/msvc10/emscripten_api_browser.vcxproj b/tests/msvc10/emscripten_api_browser.vcxproj
index 9f4a05fd..f1290f02 100644
--- a/tests/msvc10/emscripten_api_browser.vcxproj
+++ b/tests/msvc10/emscripten_api_browser.vcxproj
@@ -80,6 +80,18 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ <DisableWarnings>vexing-parse</DisableWarnings>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ <DisableWarnings>vexing-parse</DisableWarnings>
+ </ClCompile>
+ </ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\emscripten_api_browser.cpp" />
</ItemGroup>
diff --git a/tests/msvc10/glbook_10_MultiTexture.vcxproj b/tests/msvc10/glbook_10_MultiTexture.vcxproj
index a831f351..86167eb3 100644
--- a/tests/msvc10/glbook_10_MultiTexture.vcxproj
+++ b/tests/msvc10/glbook_10_MultiTexture.vcxproj
@@ -83,11 +83,13 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
diff --git a/tests/msvc10/glbook_11_Multisample.vcxproj b/tests/msvc10/glbook_11_Multisample.vcxproj
index 47d5fb4a..3b7becb1 100644
--- a/tests/msvc10/glbook_11_Multisample.vcxproj
+++ b/tests/msvc10/glbook_11_Multisample.vcxproj
@@ -83,11 +83,13 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
diff --git a/tests/msvc10/glbook_11_Stencil_Test.vcxproj b/tests/msvc10/glbook_11_Stencil_Test.vcxproj
index 3a541128..654f4a8e 100644
--- a/tests/msvc10/glbook_11_Stencil_Test.vcxproj
+++ b/tests/msvc10/glbook_11_Stencil_Test.vcxproj
@@ -83,11 +83,13 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
diff --git a/tests/msvc10/glbook_13_ParticleSystem.vcxproj b/tests/msvc10/glbook_13_ParticleSystem.vcxproj
index c18e17ff..b8648f93 100644
--- a/tests/msvc10/glbook_13_ParticleSystem.vcxproj
+++ b/tests/msvc10/glbook_13_ParticleSystem.vcxproj
@@ -83,11 +83,13 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
diff --git a/tests/msvc10/glbook_15_Hello_Triangle_KD.vcxproj b/tests/msvc10/glbook_15_Hello_Triangle_KD.vcxproj
index 489628f5..5d3026a2 100644
--- a/tests/msvc10/glbook_15_Hello_Triangle_KD.vcxproj
+++ b/tests/msvc10/glbook_15_Hello_Triangle_KD.vcxproj
@@ -83,11 +83,13 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
diff --git a/tests/msvc10/glbook_2_Hello_Triangle.vcxproj b/tests/msvc10/glbook_2_Hello_Triangle.vcxproj
index 34de0780..7a26f304 100644
--- a/tests/msvc10/glbook_2_Hello_Triangle.vcxproj
+++ b/tests/msvc10/glbook_2_Hello_Triangle.vcxproj
@@ -93,11 +93,13 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/tests/msvc10/glbook_8_Simple_VertexShader.vcxproj b/tests/msvc10/glbook_8_Simple_VertexShader.vcxproj
index 19442df9..37b07d9d 100644
--- a/tests/msvc10/glbook_8_Simple_VertexShader.vcxproj
+++ b/tests/msvc10/glbook_8_Simple_VertexShader.vcxproj
@@ -83,11 +83,13 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
diff --git a/tests/msvc10/glbook_9_MipMap2D.vcxproj b/tests/msvc10/glbook_9_MipMap2D.vcxproj
index af0e072b..2cb190b6 100644
--- a/tests/msvc10/glbook_9_MipMap2D.vcxproj
+++ b/tests/msvc10/glbook_9_MipMap2D.vcxproj
@@ -83,11 +83,13 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
diff --git a/tests/msvc10/glbook_9_Simple_Texture2D.vcxproj b/tests/msvc10/glbook_9_Simple_Texture2D.vcxproj
index 09ff3e1e..fe44f617 100644
--- a/tests/msvc10/glbook_9_Simple_Texture2D.vcxproj
+++ b/tests/msvc10/glbook_9_Simple_Texture2D.vcxproj
@@ -83,11 +83,13 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
diff --git a/tests/msvc10/glbook_9_Simple_TextureCubemap.vcxproj b/tests/msvc10/glbook_9_Simple_TextureCubemap.vcxproj
index d89da1c7..d84142a9 100644
--- a/tests/msvc10/glbook_9_Simple_TextureCubemap.vcxproj
+++ b/tests/msvc10/glbook_9_Simple_TextureCubemap.vcxproj
@@ -83,11 +83,13 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
diff --git a/tests/msvc10/glbook_9_TextureWrap.vcxproj b/tests/msvc10/glbook_9_TextureWrap.vcxproj
index 54f51e26..7f710fba 100644
--- a/tests/msvc10/glbook_9_TextureWrap.vcxproj
+++ b/tests/msvc10/glbook_9_TextureWrap.vcxproj
@@ -83,11 +83,13 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign;int-conversion</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
<ClCompile>
<AdditionalIncludeDirectories>../glbook/Common</AdditionalIncludeDirectories>
+ <DisableWarnings>pointer-sign;int-conversion</DisableWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
diff --git a/tests/msvc10/new.vcxproj b/tests/msvc10/new.vcxproj
deleted file mode 100644
index 11ad95ae..00000000
--- a/tests/msvc10/new.vcxproj
+++ /dev/null
@@ -1,89 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|Emscripten">
- <Configuration>Debug</Configuration>
- <Platform>Emscripten</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Debug|Win32">
- <Configuration>Debug</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|Emscripten">
- <Configuration>Release</Configuration>
- <Platform>Emscripten</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|Win32">
- <Configuration>Release</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <ProjectGuid>{5190107D-91B3-4EF8-82CB-08381DD19ABB}</ProjectGuid>
- <RootNamespace>new</RootNamespace>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
- <PlatformToolset>emcc</PlatformToolset>
- <ConfigurationType>HTMLPage</ConfigurationType>
- </PropertyGroup>
- <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
- <PlatformToolset>emcc</PlatformToolset>
- <ConfigurationType>HTMLPage</ConfigurationType>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Label="ExtensionSettings">
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
- <IntDir>$(Platform)\$(ProjectName)_$(Configuration)\</IntDir>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
- <IntDir>$(Platform)\$(ProjectName)_$(Configuration)\</IntDir>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- </Link>
- </ItemDefinitionGroup>
- <ItemGroup>
- <ClCompile Include="..\new.cpp" />
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
-</Project> \ No newline at end of file
diff --git a/tests/msvc10/sdl_audio.vcxproj b/tests/msvc10/sdl_audio.vcxproj
index 80a48d90..7f53853b 100644
--- a/tests/msvc10/sdl_audio.vcxproj
+++ b/tests/msvc10/sdl_audio.vcxproj
@@ -80,6 +80,22 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>--preload-file ../sounds/alarmvictory_1.ogg@/sound.ogg --preload-file ../sounds/alarmcreatemiltaryfoot_1.wav@/sound2.wav --preload-file ../sounds/noise.ogg@/ --embed-file ../sounds/the_entertainer.ogg@/ --preload-file ../runner.py@/bad.ogg %(AdditionalOptions)</AdditionalOptions>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>--preload-file ../sounds/alarmvictory_1.ogg@/sound.ogg --preload-file ../sounds/alarmcreatemiltaryfoot_1.wav@/sound2.wav --preload-file ../sounds/noise.ogg@/ --embed-file ../sounds/the_entertainer.ogg@/ --preload-file ../runner.py@/bad.ogg %(AdditionalOptions)</AdditionalOptions>
+ </Link>
+ </ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\sdl_audio.c" />
</ItemGroup>
diff --git a/tests/msvc10/sdl_canvas.vcxproj b/tests/msvc10/sdl_canvas.vcxproj
index 42215b38..91fd1dd5 100644
--- a/tests/msvc10/sdl_canvas.vcxproj
+++ b/tests/msvc10/sdl_canvas.vcxproj
@@ -83,6 +83,16 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
diff --git a/tests/msvc10/sdl_gl_read.vcxproj b/tests/msvc10/sdl_gl_read.vcxproj
index de6233c1..8ba31677 100644
--- a/tests/msvc10/sdl_gl_read.vcxproj
+++ b/tests/msvc10/sdl_gl_read.vcxproj
@@ -80,6 +80,18 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ <DisableWarnings>pointer-sign</DisableWarnings>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ <DisableWarnings>pointer-sign</DisableWarnings>
+ </ClCompile>
+ </ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\sdl_gl_read.c" />
</ItemGroup>
diff --git a/tests/msvc10/sdl_image.vcxproj b/tests/msvc10/sdl_image.vcxproj
index 631ea5cd..543d6976 100644
--- a/tests/msvc10/sdl_image.vcxproj
+++ b/tests/msvc10/sdl_image.vcxproj
@@ -80,6 +80,23 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy;SCREENSHOT_DIRNAME=\"/assets\";SCREENSHOT_BASENAME=\"screenshot.jpg\"</PreprocessorDefinitions>
+ <EchoCommandLines>true</EchoCommandLines>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>--preload-file ../screenshot.jpg@/assets/ %(AdditionalOptions)</AdditionalOptions>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy;SCREENSHOT_DIRNAME=\"/assets\";SCREENSHOT_BASENAME=\"screenshot.jpg\"</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>--preload-file ../screenshot.jpg@/assets/ %(AdditionalOptions)</AdditionalOptions>
+ </Link>
+ </ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\sdl_image.c" />
</ItemGroup>
diff --git a/tests/msvc10/sdl_key.vcxproj b/tests/msvc10/sdl_key.vcxproj
index 3840256a..cb7b888e 100644
--- a/tests/msvc10/sdl_key.vcxproj
+++ b/tests/msvc10/sdl_key.vcxproj
@@ -80,6 +80,16 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\sdl_key.c" />
</ItemGroup>
diff --git a/tests/msvc10/sdl_mouse.vcxproj b/tests/msvc10/sdl_mouse.vcxproj
index 7d582eb3..f3336a29 100644
--- a/tests/msvc10/sdl_mouse.vcxproj
+++ b/tests/msvc10/sdl_mouse.vcxproj
@@ -80,6 +80,16 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\sdl_mouse.c" />
</ItemGroup>
diff --git a/tests/msvc10/sdl_ogl.vcxproj b/tests/msvc10/sdl_ogl.vcxproj
index b96eac4d..fc8bdb4d 100644
--- a/tests/msvc10/sdl_ogl.vcxproj
+++ b/tests/msvc10/sdl_ogl.vcxproj
@@ -80,6 +80,22 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>-s LEGACY_GL_EMULATION=1 --preload-file ../screenshot.png@/ %(AdditionalOptions)</AdditionalOptions>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
+ <ClCompile>
+ <PreprocessorDefinitions>REPORT_RESULT=int dummy</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>-s LEGACY_GL_EMULATION=1 %(AdditionalOptions)</AdditionalOptions>
+ </Link>
+ </ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\sdl_ogl.c" />
</ItemGroup>
diff --git a/tests/msvc10/tests_msvc10.sln b/tests/msvc10/tests_msvc10.sln
index 67333016..e606469b 100644
--- a/tests/msvc10/tests_msvc10.sln
+++ b/tests/msvc10/tests_msvc10.sln
@@ -53,8 +53,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "files", "files.vcxproj", "{
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hashtest", "hashtest.vcxproj", "{61D7F11F-25EE-4C2C-9D73-8601F68B055E}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "new", "new.vcxproj", "{5190107D-91B3-4EF8-82CB-08381DD19ABB}"
-EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdl_audio", "sdl_audio.vcxproj", "{F28A1DE1-5949-4AF5-8901-A37871C2514E}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdl_canvas", "sdl_canvas.vcxproj", "{CE360D01-4362-4FE4-A77E-8EF6E3F623CF}"
@@ -196,10 +194,6 @@ Global
{61D7F11F-25EE-4C2C-9D73-8601F68B055E}.Debug|Emscripten.Build.0 = Debug|Emscripten
{61D7F11F-25EE-4C2C-9D73-8601F68B055E}.Release|Emscripten.ActiveCfg = Release|Emscripten
{61D7F11F-25EE-4C2C-9D73-8601F68B055E}.Release|Emscripten.Build.0 = Release|Emscripten
- {5190107D-91B3-4EF8-82CB-08381DD19ABB}.Debug|Emscripten.ActiveCfg = Debug|Emscripten
- {5190107D-91B3-4EF8-82CB-08381DD19ABB}.Debug|Emscripten.Build.0 = Debug|Emscripten
- {5190107D-91B3-4EF8-82CB-08381DD19ABB}.Release|Emscripten.ActiveCfg = Release|Emscripten
- {5190107D-91B3-4EF8-82CB-08381DD19ABB}.Release|Emscripten.Build.0 = Release|Emscripten
{F28A1DE1-5949-4AF5-8901-A37871C2514E}.Debug|Emscripten.ActiveCfg = Debug|Emscripten
{F28A1DE1-5949-4AF5-8901-A37871C2514E}.Debug|Emscripten.Build.0 = Debug|Emscripten
{F28A1DE1-5949-4AF5-8901-A37871C2514E}.Release|Emscripten.ActiveCfg = Release|Emscripten
@@ -270,7 +264,6 @@ Global
{65E0ED61-A813-4AEB-8DB0-A58657858EE2} = {8E55380B-7067-47FF-8B6B-6D656B8D8CC3}
{BE6A123E-9729-44A3-976F-3C06A3724894} = {8E55380B-7067-47FF-8B6B-6D656B8D8CC3}
{61D7F11F-25EE-4C2C-9D73-8601F68B055E} = {8E55380B-7067-47FF-8B6B-6D656B8D8CC3}
- {5190107D-91B3-4EF8-82CB-08381DD19ABB} = {8E55380B-7067-47FF-8B6B-6D656B8D8CC3}
{678A07B3-3A25-40E4-8A36-7A399056188A} = {8E55380B-7067-47FF-8B6B-6D656B8D8CC3}
{C896D890-9132-4A2D-8BA8-0EB6888FEAC2} = {8E55380B-7067-47FF-8B6B-6D656B8D8CC3}
{C29BBEE3-02D1-459A-B8BA-832A3439F12D} = {8E55380B-7067-47FF-8B6B-6D656B8D8CC3}
diff --git a/tests/msvc10/twopart.vcxproj b/tests/msvc10/twopart.vcxproj
index 7d299984..72cc0640 100644
--- a/tests/msvc10/twopart.vcxproj
+++ b/tests/msvc10/twopart.vcxproj
@@ -80,6 +80,16 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Emscripten'">
+ <ClCompile>
+ <DisableWarnings>deprecated-writable-strings</DisableWarnings>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Emscripten'">
+ <ClCompile>
+ <DisableWarnings>deprecated-writable-strings</DisableWarnings>
+ </ClCompile>
+ </ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\twopart_main.cpp" />
<ClCompile Include="..\twopart_side.cpp" />
diff --git a/tests/runner.py b/tests/runner.py
index bbbc23e5..f0e61c4e 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -63,6 +63,10 @@ class RunnerCore(unittest.TestCase):
self.working_dir = dirname
os.chdir(dirname)
+ # Use emscripten root for node module lookup
+ scriptdir = os.path.dirname(os.path.abspath(__file__))
+ os.environ['NODE_PATH'] = os.path.join(scriptdir, '..', 'node_modules')
+
if not self.save_dir:
self.has_prev_ll = False
for temp_file in os.listdir(TEMP_DIR):
diff --git a/tests/sockets/test_getaddrinfo.c b/tests/sockets/test_getaddrinfo.c
new file mode 100644
index 00000000..717a9ae7
--- /dev/null
+++ b/tests/sockets/test_getaddrinfo.c
@@ -0,0 +1,197 @@
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <assert.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+int main() {
+ struct addrinfo hints;
+ struct addrinfo *servinfo;
+ struct sockaddr_in *sa4;
+ struct sockaddr_in6 *sa6;
+ int err;
+
+ // no name or service
+ err = getaddrinfo(NULL, NULL, NULL, &servinfo);
+ assert(err == EAI_NONAME);
+
+ // invalid socket type
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = 9999;
+ err = getaddrinfo("www.mozilla.org", "80", &hints, &servinfo);
+#ifdef __APPLE__
+ assert(err == EAI_BADHINTS);
+#else
+ assert(err == EAI_SOCKTYPE);
+#endif
+
+ // invalid family
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNIX;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo("www.mozilla.org", "80", &hints, &servinfo);
+ assert(err == EAI_FAMILY);
+
+ // invalid service
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo("www.mozilla.org", "foobar", &hints, &servinfo);
+#ifdef __APPLE__
+ assert(err == EAI_NONAME);
+#else
+ assert(err == EAI_SERVICE);
+#endif
+
+ // test loopback resolution (ipv4)
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo(NULL, "80", &hints, &servinfo);
+ assert(!err);
+ sa4 = ((struct sockaddr_in*)servinfo->ai_addr);
+ assert(servinfo->ai_family == AF_INET);
+ assert(servinfo->ai_socktype == SOCK_STREAM);
+ assert(*(uint32_t*)&(sa4->sin_addr) == ntohl(INADDR_LOOPBACK));
+ assert(sa4->sin_port == ntohs(80));
+ freeaddrinfo(servinfo);
+
+ // test loopback resolution (ipv6)
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo(NULL, "81", &hints, &servinfo);
+ assert(!err);
+ sa6 = ((struct sockaddr_in6*)servinfo->ai_addr);
+ assert(servinfo->ai_family == AF_INET6);
+ assert(servinfo->ai_socktype == SOCK_STREAM);
+ memcmp(&sa6->sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback));
+ assert(sa6->sin6_port == ntohs(81));
+ freeaddrinfo(servinfo);
+
+ // test bind preparation
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo(NULL, "82", &hints, &servinfo);
+ assert(!err);
+ sa4 = ((struct sockaddr_in*)servinfo->ai_addr);
+ assert(servinfo->ai_family == AF_INET);
+ assert(servinfo->ai_socktype == SOCK_STREAM);
+ assert(*(uint32_t*)&(sa4->sin_addr) == 0);
+ assert(sa4->sin_port == ntohs(82));
+ freeaddrinfo(servinfo);
+
+ // test numeric address (ipv4)
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ err = getaddrinfo("1.2.3.4", "83", &hints, &servinfo);
+ assert(!err);
+ sa4 = ((struct sockaddr_in*)servinfo->ai_addr);
+ assert(servinfo->ai_family == AF_INET);
+ assert(servinfo->ai_socktype == SOCK_DGRAM);
+ assert(*(uint32_t*)&(sa4->sin_addr) == 67305985);
+ assert(sa4->sin_port == ntohs(83));
+ freeaddrinfo(servinfo);
+
+ // test numeric address (ipv4 address specified as ipv6 with AI_V4MAPPED)
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_V4MAPPED;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo("1.2.3.4", "84", &hints, &servinfo);
+ assert(!err);
+ sa6 = ((struct sockaddr_in6*)servinfo->ai_addr);
+ assert(servinfo->ai_family == AF_INET6);
+ assert(servinfo->ai_socktype == SOCK_STREAM);
+ assert(*((uint32_t*)&(sa6->sin6_addr)+2) == htonl(0xffff));
+ assert(*((uint32_t*)&(sa6->sin6_addr)+3) == 67305985);
+ assert(sa6->sin6_port == ntohs(84));
+ freeaddrinfo(servinfo);
+
+ // test numeric address (ipv4 address specified as ipv6 without AI_V4MAPPED)
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo("1.2.3.4", "85", &hints, &servinfo);
+#ifdef __linux__
+ assert(err == -9 /* EAI_ADDRFAMILY */);
+#else
+ assert(err == EAI_NONAME);
+#endif
+
+ // test numeric address (ipv6)
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ err = getaddrinfo("2001:0db8:85a3:0042:1000:8a2e:0370:7334", "86", &hints, &servinfo);
+ assert(!err);
+ sa6 = ((struct sockaddr_in6*)servinfo->ai_addr);
+ assert(servinfo->ai_family == AF_INET6);
+ assert(servinfo->ai_socktype == SOCK_DGRAM);
+ assert(*((uint32_t*)&(sa6->sin6_addr)+0) == -1207107296);
+ assert(*((uint32_t*)&(sa6->sin6_addr)+1) == 1107338117);
+ assert(*((uint32_t*)&(sa6->sin6_addr)+2) == 780795920);
+ assert(*((uint32_t*)&(sa6->sin6_addr)+3) == 879980547);
+ assert(sa6->sin6_port == ntohs(86));
+ freeaddrinfo(servinfo);
+
+ // test numeric address (ipv6 address specified as ipv4)
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo("2001:0db8:85a3:0042:1000:8a2e:0370:7334", "87", &hints, &servinfo);
+#ifdef __linux__
+ assert(err == -9 /* EAI_ADDRFAMILY */);
+#else
+ assert(err == EAI_NONAME);
+#endif
+
+ // test non-numeric host with AI_NUMERICHOST
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo("www.mozilla.org", "88", &hints, &servinfo);
+ assert(err == EAI_NONAME);
+
+ // test non-numeric host with AF_INET
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo("www.mozilla.org", "89", &hints, &servinfo);
+ assert(!err);
+ sa4 = ((struct sockaddr_in*)servinfo->ai_addr);
+ assert(servinfo->ai_family == AF_INET);
+ assert(servinfo->ai_socktype == SOCK_STREAM);
+ assert(sa4->sin_port == ntohs(89));
+
+ // test non-numeric host with AF_INET6
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo("www.mozilla.org", "90", &hints, &servinfo);
+ assert(!err);
+ sa6 = ((struct sockaddr_in6*)servinfo->ai_addr);
+ assert(servinfo->ai_family == AF_INET6);
+ assert(servinfo->ai_socktype == SOCK_STREAM);
+ assert(*((uint32_t*)&(sa6->sin6_addr)+0) != 0 ||
+ *((uint32_t*)&(sa6->sin6_addr)+1) != 0 ||
+ *((uint32_t*)&(sa6->sin6_addr)+2) != 0 ||
+ *((uint32_t*)&(sa6->sin6_addr)+3) != 0);
+ assert(sa6->sin6_port == ntohs(90));
+
+ puts("success");
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/tests/sockets/test_sockets_gethostbyname.c b/tests/sockets/test_gethostbyname.c
index 12fc6d9d..de7da706 100644
--- a/tests/sockets/test_sockets_gethostbyname.c
+++ b/tests/sockets/test_gethostbyname.c
@@ -11,16 +11,6 @@
#include <emscripten.h>
#endif
-int sockfd;
-
-void finish(int result) {
- close(sockfd);
-#if EMSCRIPTEN
- REPORT_RESULT();
-#endif
- exit(result);
-}
-
int main() {
char str[INET_ADDRSTRLEN];
struct in_addr addr;
@@ -29,6 +19,8 @@ int main() {
// resolve the hostname ot an actual address
struct hostent *host = gethostbyname("slashdot.org");
+ assert(host->h_addrtype == AF_INET);
+ assert(host->h_length == sizeof(uint32_t));
// convert the raw address to a string
char **raw_addr_list = host->h_addr_list;
@@ -44,6 +36,8 @@ int main() {
struct hostent *host1 = gethostbyaddr(&addr, sizeof(addr), host->h_addrtype);
assert(strstr(host1->h_name, "slashdot.org"));
+ puts("success");
+
return EXIT_SUCCESS;
}
diff --git a/tests/sockets/test_getnameinfo.c b/tests/sockets/test_getnameinfo.c
new file mode 100644
index 00000000..c3fec6b4
--- /dev/null
+++ b/tests/sockets/test_getnameinfo.c
@@ -0,0 +1,101 @@
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <assert.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+int main() {
+ struct addrinfo hints;
+ struct addrinfo *servinfo;
+ struct sockaddr_in sa4;
+ struct sockaddr_in6 sa6;
+ char node[256];
+ char serv[256];
+ int flags;
+ int err;
+
+#ifndef __APPLE__
+ // incorrect sockaddr size
+ memset(&sa4, 0, sizeof(sa4));
+ sa4.sin_family = AF_INET;
+ err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4)-1, NULL, 0, NULL, 0, 0);
+ assert(err == EAI_FAMILY);
+
+ memset(&sa6, 0, sizeof(sa6));
+ sa6.sin6_family = AF_INET6;
+ err = getnameinfo((struct sockaddr*)&sa6, sizeof(sa6)-1, NULL, 0, NULL, 0, 0);
+ assert(err == EAI_FAMILY);
+
+ // invalid family
+ memset(&sa4, 0, sizeof(sa4));
+ sa4.sin_family = 9999;
+ err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), NULL, 0, NULL, 0, 0);
+ assert(err == EAI_FAMILY);
+#endif
+
+ // NI_NUMERICHOST and NI_NAMEREQD conflict
+ memset(&sa4, 0, sizeof(sa4));
+ sa4.sin_family = AF_INET;
+ flags = NI_NUMERICHOST | NI_NAMEREQD;
+ err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), node, sizeof(node), serv, sizeof(serv), flags);
+ assert(err == EAI_NONAME);
+
+ // too small of buffer
+ memset(&sa4, 0, sizeof(sa4));
+ sa4.sin_family = AF_INET;
+ *(uint32_t*)&sa4.sin_addr = 67305985;
+ sa4.sin_port = htons(54321);
+ flags = NI_NUMERICHOST;
+ err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), node, 1, serv, sizeof(serv), flags);
+ assert(err == EAI_OVERFLOW);
+ err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), node, sizeof(node), serv, 1, flags);
+ assert(err == EAI_OVERFLOW);
+
+ // NI_NAMEREQD and lookup failed
+ memset(&sa4, 0, sizeof(sa4));
+ sa4.sin_family = AF_INET;
+ *(uint32_t*)&sa4.sin_addr = 67305985;
+ flags = NI_NAMEREQD;
+ err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), node, sizeof(node), serv, sizeof(serv), flags);
+ assert(err == EAI_NONAME);
+
+ // lookup failed
+ memset(&sa4, 0, sizeof(sa4));
+ sa4.sin_family = AF_INET;
+ *(uint32_t*)&sa4.sin_addr = 67305985;
+ err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), node, sizeof(node), serv, sizeof(serv), flags);
+ assert(err == EAI_NONAME);
+
+ // no lookup
+ memset(&sa4, 0, sizeof(sa4));
+ sa4.sin_family = AF_INET;
+ *(uint32_t*)&sa4.sin_addr = 67305985;
+ sa4.sin_port = htons(54321);
+ flags = NI_NUMERICHOST;
+ err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), node, sizeof(node), serv, sizeof(serv), flags);
+ assert(!err);
+ assert(!strcmp(node, "1.2.3.4"));
+ assert(!strcmp(serv, "54321"));
+
+ // lookup
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo("www.mozilla.org", "54321", &hints, &servinfo);
+ assert(!err);
+ flags = NI_NAMEREQD;
+ err = getnameinfo(servinfo->ai_addr, servinfo->ai_addrlen, node, sizeof(node), serv, sizeof(serv), flags);
+ assert(!err);
+ assert(strstr(node, "mozilla"));
+ assert(!strcmp(serv, "54321"));
+
+ puts("success");
+
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/tests/sockets/test_sockets_echo_client.c b/tests/sockets/test_sockets_echo_client.c
index 6b3ccef3..f6ea85cf 100644
--- a/tests/sockets/test_sockets_echo_client.c
+++ b/tests/sockets/test_sockets_echo_client.c
@@ -38,7 +38,10 @@ int echo_read;
int echo_wrote;
void finish(int result) {
- close(server.fd);
+ if (server.fd) {
+ close(server.fd);
+ server.fd = 0;
+ }
#if EMSCRIPTEN
REPORT_RESULT();
#endif
@@ -76,7 +79,12 @@ void main_loop(void *arg) {
assert(available);
res = do_msg_read(server.fd, &server.msg, echo_read, 0, NULL, NULL);
- if (res != -1) echo_read += res;
+ if (res == 0) {
+ perror("server closed");
+ finish(EXIT_FAILURE);
+ } else if (res != -1) {
+ echo_read += res;
+ }
// once we've read the entire message, validate it
if (echo_read >= server.msg.length) {
@@ -89,7 +97,12 @@ void main_loop(void *arg) {
}
res = do_msg_write(server.fd, &echo_msg, echo_wrote, 0, NULL, 0);
- if (res != -1) echo_wrote += res;
+ if (res == 0) {
+ perror("server closed");
+ finish(EXIT_FAILURE);
+ } else if (res != -1) {
+ echo_wrote += res;
+ }
// once we're done writing the message, read it back
if (echo_wrote >= echo_msg.length) {
diff --git a/tests/sockets/test_sockets_echo_server.c b/tests/sockets/test_sockets_echo_server.c
index f01004c3..dbc912a6 100644
--- a/tests/sockets/test_sockets_echo_server.c
+++ b/tests/sockets/test_sockets_echo_server.c
@@ -1,6 +1,7 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -38,8 +39,14 @@ server_t server;
client_t client;
void cleanup() {
- if (server.fd) close(server.fd);
- if (client.fd) close(client.fd);
+ if (client.fd) {
+ close(client.fd);
+ client.fd = 0;
+ }
+ if (server.fd) {
+ close(server.fd);
+ server.fd = 0;
+ }
}
void main_loop(void *arg) {
@@ -83,7 +90,13 @@ void main_loop(void *arg) {
}
res = do_msg_read(fd, &client.msg, client.read, 0, (struct sockaddr *)&client.addr, &addrlen);
- if (res != -1) client.read += res;
+ if (res == 0) {
+ // client disconnected
+ memset(&client, 0, sizeof(client_t));
+ return;
+ } else if (res != -1) {
+ client.read += res;
+ }
// once we've read the entire message, echo it back
if (client.read >= client.msg.length) {
@@ -96,12 +109,22 @@ void main_loop(void *arg) {
}
res = do_msg_write(fd, &client.msg, client.wrote, 0, (struct sockaddr *)&client.addr, sizeof(client.addr));
- if (res != -1) client.wrote += res;
+ if (res == 0) {
+ // client disconnected
+ memset(&client, 0, sizeof(client_t));
+ return;
+ } else if (res != -1) {
+ client.wrote += res;
+ }
- // close the client once we've echo'd back the entire message
if (client.wrote >= client.msg.length) {
+ client.wrote = 0;
+ client.state = MSG_READ;
+
+#if CLOSE_CLIENT_AFTER_ECHO
close(client.fd);
memset(&client, 0, sizeof(client_t));
+#endif
}
}
}
@@ -111,7 +134,7 @@ int main() {
int res;
atexit(cleanup);
- //signal(SIGTERM, cleanup);
+ signal(SIGTERM, cleanup);
memset(&server, 0, sizeof(server_t));
memset(&client, 0, sizeof(client_t));
diff --git a/tests/sockets/test_sockets_msg.h b/tests/sockets/test_sockets_msg.h
index 30094d65..0e68803c 100644
--- a/tests/sockets/test_sockets_msg.h
+++ b/tests/sockets/test_sockets_msg.h
@@ -17,9 +17,10 @@ int do_msg_read(int sockfd, msg_t *msg, int offset, int length, struct sockaddr
return res;
}
assert(res != 0);
- msg->buffer = malloc(msg->length);
printf("do_msg_read: allocating %d bytes for message\n", msg->length);
+
+ msg->buffer = malloc(msg->length);
}
// read the actual message
@@ -52,6 +53,7 @@ int do_msg_write(int sockfd, msg_t *msg, int offset, int length, struct sockaddr
assert(errno == EAGAIN);
return res;
}
+ printf("do_msg_write: sending message header for %d bytes\n", msg->length);
assert(res == sizeof(int));
}
diff --git a/tests/sockets/test_sockets_partial_server.c b/tests/sockets/test_sockets_partial_server.c
index 44ad40a3..19f7f2af 100644
--- a/tests/sockets/test_sockets_partial_server.c
+++ b/tests/sockets/test_sockets_partial_server.c
@@ -1,6 +1,7 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -18,8 +19,14 @@ int serverfd = 0;
int clientfd = 0;
void cleanup() {
- if (serverfd) close(serverfd);
- if (clientfd) close(clientfd);
+ if (serverfd) {
+ close(serverfd);
+ serverfd = 0;
+ }
+ if (clientfd) {
+ close(clientfd);
+ clientfd = 0;
+ }
}
void do_send(int sockfd) {
@@ -86,7 +93,7 @@ int main() {
int res;
atexit(cleanup);
- //signal(SIGTERM, cleanup);
+ signal(SIGTERM, cleanup);
// create the socket
serverfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
diff --git a/tests/sockets/test_sockets_select_server_closes_connection_client_rw.c b/tests/sockets/test_sockets_select_server_closes_connection_client_rw.c
index 198ad232..25dcdd05 100644
--- a/tests/sockets/test_sockets_select_server_closes_connection_client_rw.c
+++ b/tests/sockets/test_sockets_select_server_closes_connection_client_rw.c
@@ -89,7 +89,12 @@ void main_loop(void *arg) {
// read a single byte
transferAmount = do_msg_read(sockfd, &readmsg, readPos, 1, NULL, NULL);
- if (transferAmount != -1) readPos += transferAmount;
+ if (transferAmount == 0) {
+ perror("server closed");
+ finish(EXIT_FAILURE);
+ } else if (transferAmount != -1) {
+ readPos += transferAmount;
+ }
// if successfully reading 1 byte, switch to next state
if (readPos >= 1) {
@@ -122,7 +127,12 @@ void main_loop(void *arg) {
// read a single byte
transferAmount = do_msg_read(sockfd, &readmsg, readPos, 1, NULL, NULL);
- if (transferAmount != -1) readPos += transferAmount;
+ if (transferAmount == 0) {
+ perror("server closed");
+ finish(EXIT_FAILURE);
+ } else if (transferAmount != -1) {
+ readPos += transferAmount;
+ }
// with 10 bytes read the inQueue is empty => switch state
if (readPos >= readmsg.length) {
diff --git a/tests/sockets/test_sockets_select_server_down_client.c b/tests/sockets/test_sockets_select_server_down_client.c
index e05bd4c8..27e200e0 100644
--- a/tests/sockets/test_sockets_select_server_down_client.c
+++ b/tests/sockets/test_sockets_select_server_down_client.c
@@ -48,7 +48,7 @@ void iter(void *arg) {
char buffer[1024];
int n = recv(sockfd, buffer, sizeof(buffer), 0);
if (n == -1 && retries++ > 10) {
- perror("revv failed");
+ perror("recv failed");
finish(EXIT_FAILURE);
} else if (!n) {
perror("Connection to websocket server failed as expected.");
diff --git a/tests/termios/test_tcgetattr.c b/tests/termios/test_tcgetattr.c
new file mode 100644
index 00000000..2b3780ee
--- /dev/null
+++ b/tests/termios/test_tcgetattr.c
@@ -0,0 +1,61 @@
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+static void create_file(const char *path, const char *buffer, int mode) {
+ int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+ assert(fd >= 0);
+
+ int err = write(fd, buffer, sizeof(char) * strlen(buffer));
+ assert(err == (sizeof(char) * strlen(buffer)));
+
+ close(fd);
+}
+
+void setup() {
+ create_file("test.txt", "abcdefg", 0666);
+}
+
+void cleanup() {
+ unlink("test.txt");
+}
+
+void test() {
+ struct termios tc;
+ int ret;
+ int fd;
+
+ fd = open("test.txt", O_RDONLY);
+
+ ret = tcgetattr(fd, &tc);
+ assert(ret == -1);
+ assert(errno = ENOTTY);
+
+ ret = tcgetattr(STDIN_FILENO, &tc);
+ assert(!ret);
+
+ ret = tcsetattr(fd, 0, &tc);
+ assert(ret == -1);
+ assert(errno = ENOTTY);
+
+ ret = tcsetattr(STDIN_FILENO, 0, &tc);
+ assert(!ret);
+
+ close(fd);
+
+ puts("success");
+}
+
+int main() {
+ atexit(cleanup);
+ signal(SIGABRT, cleanup);
+ setup();
+ test();
+ return EXIT_SUCCESS;
+}
diff --git a/tests/test_browser.py b/tests/test_browser.py
index a35ce177..7f085f15 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -794,6 +794,9 @@ If manually bisecting:
def test_glut_touchevents(self):
self.btest('glut_touchevents.c', '1')
+ def test_emscripten_get_now(self):
+ self.btest('emscripten_get_now.cpp', '1')
+
def test_file_db(self):
secret = str(time.time())
open('moar.txt', 'w').write(secret)
@@ -1404,3 +1407,8 @@ If manually bisecting:
def test_emscripten_async_wget2(self):
self.btest('http.cpp', expected='0', args=['-I' + path_from_root('tests')])
+
+ def test_module(self):
+ Popen([PYTHON, EMCC, path_from_root('tests', 'browser_module.cpp'), '-o', 'module.js', '-O2', '-s', 'SIDE_MODULE=1', '-s', 'DLOPEN_SUPPORT=1', '-s', 'EXPORTED_FUNCTIONS=["_one", "_two"]']).communicate()
+ self.btest('browser_main.cpp', args=['-O2', '-s', 'MAIN_MODULE=1', '-s', 'DLOPEN_SUPPORT=1'], expected='8')
+
diff --git a/tests/test_core.py b/tests/test_core.py
index 17707c44..6bb5ccb6 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -3734,6 +3734,13 @@ def process(filename):
Settings.EXPORT_ALL = 1
self.do_run(src, 'hello world!\n*100*\n*fivesix*\nmann\n', post_build=check)
+ def test_emscripten_get_now(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
+
+ if self.run_name == 'o2':
+ 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_inlinejs(self):
if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm')
src = r'''
@@ -4670,6 +4677,10 @@ The current type of b is: 9
expected = open(path_from_root('tests', 'pthread', 'specific.c.txt'), 'r').read()
self.do_run(src, expected, force_c=True)
+ 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):
# XXX Not sure what the right output is here. Looks like the test started failing with daylight savings changes. Modified it to pass again.
src = open(path_from_root('tests', 'time', 'src.c'), 'r').read()
@@ -5569,11 +5580,53 @@ The current type of b is: 9
Settings.RUNTIME_LINKED_LIBS = ['liblib.so'];
self.do_run(main, 'supp: 54,2\nmain: 56\nsupp see: 543\nmain see: 76\nok.')
- def test_dlfcn_basic(self):
- if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
+ def can_dlfcn(self):
+ if self.emcc_args and '--memory-init-file' in self.emcc_args:
+ for i in range(len(self.emcc_args)):
+ if self.emcc_args[i] == '--memory-init-file':
+ self.emcc_args = self.emcc_args[:i] + self.emcc_args[i+2:]
+ break
- Settings.NAMED_GLOBALS = 1
+ if Settings.ASM_JS:
+ Settings.DLOPEN_SUPPORT = 1
+ else:
+ Settings.NAMED_GLOBALS = 1
+
+ if not self.is_le32():
+ self.skip('need le32 for dlfcn support')
+ return False
+ else:
+ return True
+
+ def prep_dlfcn_lib(self):
+ if Settings.ASM_JS:
+ Settings.MAIN_MODULE = 0
+ Settings.SIDE_MODULE = 1
+ else:
+ Settings.BUILD_AS_SHARED_LIB = 1
+ Settings.INCLUDE_FULL_LIBRARY = 0
+ def prep_dlfcn_main(self):
+ if Settings.ASM_JS:
+ Settings.MAIN_MODULE = 1
+ Settings.SIDE_MODULE = 0
+ else:
+ Settings.BUILD_AS_SHARED_LIB = 0
+ Settings.INCLUDE_FULL_LIBRARY = 1
+
+ dlfcn_post_build = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
+ )
+ open(filename, 'w').write(src)
+'''
+
+ def test_dlfcn_basic(self):
+ if not self.can_dlfcn(): return
+
+ self.prep_dlfcn_lib()
lib_src = '''
#include <cstdio>
@@ -5588,10 +5641,10 @@ The current type of b is: 9
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
- Settings.BUILD_AS_SHARED_LIB = 1
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+ self.prep_dlfcn_main()
src = '''
#include <cstdio>
#include <dlfcn.h>
@@ -5610,28 +5663,17 @@ The current type of b is: 9
return 0;
}
'''
- Settings.BUILD_AS_SHARED_LIB = 0
- add_pre_run_and_checks = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
- )
- open(filename, 'w').write(src)
-'''
self.do_run(src, 'Constructing main object.\nConstructing lib object.\n',
- post_build=add_pre_run_and_checks)
+ post_build=self.dlfcn_post_build)
def test_dlfcn_qsort(self):
- if self.emcc_args is None: return self.skip('requires emcc')
- if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
-
- Settings.LINKABLE = 1
- Settings.NAMED_GLOBALS = 1
+ if not self.can_dlfcn(): return
if Settings.USE_TYPED_ARRAYS == 2:
Settings.CORRECT_SIGNS = 1 # Needed for unsafe optimizations
+ self.prep_dlfcn_lib()
+ Settings.EXPORTED_FUNCTIONS = ['_get_cmp']
lib_src = '''
int lib_cmp(const void* left, const void* right) {
const int* a = (const int*) left;
@@ -5649,11 +5691,11 @@ def process(filename):
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
- Settings.BUILD_AS_SHARED_LIB = 1
- Settings.EXPORTED_FUNCTIONS = ['_get_cmp']
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+ self.prep_dlfcn_main()
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc']
src = '''
#include <stdio.h>
#include <stdlib.h>
@@ -5675,6 +5717,13 @@ def process(filename):
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("%d ", arr[i]);
+ }
+ printf("\\n");
+
lib_handle = dlopen("liblib.so", RTLD_NOW);
if (lib_handle == NULL) {
printf("Could not load lib.\\n");
@@ -5686,14 +5735,6 @@ def process(filename):
return 1;
}
lib_cmp_ptr = getter_ptr();
-
- qsort((void*)arr, 5, sizeof(int), main_cmp);
- printf("Sort with main comparison: ");
- for (int i = 0; i < 5; i++) {
- printf("%d ", arr[i]);
- }
- printf("\\n");
-
qsort((void*)arr, 5, sizeof(int), lib_cmp_ptr);
printf("Sort with lib comparison: ");
for (int i = 0; i < 5; i++) {
@@ -5704,27 +5745,22 @@ def process(filename):
return 0;
}
'''
- Settings.BUILD_AS_SHARED_LIB = 0
- Settings.EXPORTED_FUNCTIONS = ['_main']
- add_pre_run_and_checks = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
- )
- open(filename, 'w').write(src)
-'''
self.do_run(src, 'Sort with main comparison: 5 4 3 2 1 *Sort with lib comparison: 1 2 3 4 5 *',
output_nicerizer=lambda x, err: x.replace('\n', '*'),
- post_build=add_pre_run_and_checks)
+ post_build=self.dlfcn_post_build)
+
+ if Settings.ASM_JS and os.path.exists(SPIDERMONKEY_ENGINE[0]):
+ out = run_js('liblib.so', engine=SPIDERMONKEY_ENGINE, full_output=True, stderr=STDOUT)
+ assert 'asm' in out
+ self.validate_asmjs(out)
def test_dlfcn_data_and_fptr(self):
- if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
- if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func')
+ if Settings.ASM_JS: return self.skip('this is not a valid case - libraries should not be able to access their parents globals willy nilly')
+ if not self.can_dlfcn(): return
- Settings.LINKABLE = 1
- Settings.NAMED_GLOBALS = 1
+ if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func')
+ self.prep_dlfcn_lib()
lib_src = '''
#include <stdio.h>
@@ -5749,21 +5785,23 @@ def process(filename):
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
- Settings.BUILD_AS_SHARED_LIB = 1
Settings.EXPORTED_FUNCTIONS = ['_func']
Settings.EXPORTED_GLOBALS = ['_global']
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+ self.prep_dlfcn_main()
+ Settings.LINKABLE = 1
src = '''
#include <stdio.h>
#include <dlfcn.h>
+ #include <emscripten.h>
typedef void (*FUNCTYPE(int, void(*)()))();
FUNCTYPE func;
- void parent_func() {
+ void EMSCRIPTEN_KEEPALIVE parent_func() {
printf("parent_func called from child\\n");
}
@@ -5807,23 +5845,14 @@ def process(filename):
return 0;
}
'''
- Settings.BUILD_AS_SHARED_LIB = 0
Settings.EXPORTED_FUNCTIONS = ['_main']
Settings.EXPORTED_GLOBALS = []
- add_pre_run_and_checks = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
- )
- open(filename, 'w').write(src)
-'''
self.do_run(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*parent_func called from child*parent_func called from child*Var: 42*',
output_nicerizer=lambda x, err: x.replace('\n', '*'),
- post_build=add_pre_run_and_checks)
+ post_build=self.dlfcn_post_build)
def test_dlfcn_alias(self):
- if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
+ if Settings.ASM_JS: return self.skip('this is not a valid case - libraries should not be able to access their parents globals willy nilly')
Settings.LINKABLE = 1
Settings.NAMED_GLOBALS = 1
@@ -5865,29 +5894,23 @@ def process(filename):
Settings.BUILD_AS_SHARED_LIB = 0
Settings.INCLUDE_FULL_LIBRARY = 1
Settings.EXPORTED_FUNCTIONS = ['_main']
- add_pre_run_and_checks = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
- )
- open(filename, 'w').write(src)
-'''
self.do_run(src, 'Parent global: 123.*Parent global: 456.*',
output_nicerizer=lambda x, err: x.replace('\n', '*'),
- post_build=add_pre_run_and_checks,
+ post_build=self.dlfcn_post_build,
extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/time.h,libc/langinfo.h'])
Settings.INCLUDE_FULL_LIBRARY = 0
def test_dlfcn_varargs(self):
- if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
+ if Settings.ASM_JS: return self.skip('this is not a valid case - libraries should not be able to access their parents globals willy nilly')
+
+ if not self.can_dlfcn(): return
Settings.LINKABLE = 1
- Settings.NAMED_GLOBALS = 1
if Building.LLVM_OPTS == 2: return self.skip('LLVM LTO will optimize things that prevent shared objects from working')
if Settings.QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this')
+ self.prep_dlfcn_lib()
lib_src = r'''
void print_ints(int n, ...);
extern "C" void func() {
@@ -5896,15 +5919,16 @@ def process(filename):
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
- Settings.BUILD_AS_SHARED_LIB = 1
Settings.EXPORTED_FUNCTIONS = ['_func']
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+ self.prep_dlfcn_main()
src = r'''
#include <stdarg.h>
#include <stdio.h>
#include <dlfcn.h>
+ #include <assert.h>
void print_ints(int n, ...) {
va_list args;
@@ -5922,24 +5946,16 @@ def process(filename):
print_ints(2, 100, 200);
lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle);
fptr = (void (*)())dlsym(lib_handle, "func");
fptr();
return 0;
}
'''
- Settings.BUILD_AS_SHARED_LIB = 0
Settings.EXPORTED_FUNCTIONS = ['_main']
- add_pre_run_and_checks = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
- )
- open(filename, 'w').write(src)
-'''
self.do_run(src, '100\n200\n13\n42\n',
- post_build=add_pre_run_and_checks)
+ post_build=self.dlfcn_post_build)
def test_dlfcn_self(self):
if Settings.USE_TYPED_ARRAYS == 1: return self.skip('Does not work with USE_TYPED_ARRAYS=1')
@@ -5948,14 +5964,15 @@ def process(filename):
src = r'''
#include <stdio.h>
#include <dlfcn.h>
+#include <emscripten.h>
-int global = 123;
+int EMSCRIPTEN_KEEPALIVE global = 123;
-extern "C" __attribute__((noinline)) void foo(int x) {
+extern "C" EMSCRIPTEN_KEEPALIVE void foo(int x) {
printf("%d\n", x);
}
-extern "C" __attribute__((noinline)) void repeatable() {
+extern "C" EMSCRIPTEN_KEEPALIVE void repeatable() {
void* self = dlopen(NULL, RTLD_LAZY);
int* global_ptr = (int*)dlsym(self, "global");
void (*foo_ptr)(int) = (void (*)(int))dlsym(self, "foo");
@@ -5976,15 +5993,239 @@ return 0;
break
else:
raise Exception('Could not find symbol table!')
- import json
- table = json.loads(table[table.find('{'):table.rfind('}')+1])
- actual = list(sorted(table.keys()))
+ table = table[table.find('{'):table.rfind('}')+1]
# ensure there aren't too many globals; we don't want unnamed_addr
- assert actual == ['_foo', '_global', '_main', '_repeatable'], \
- "Symbol table does not match: %s" % actual
-
+ assert table.count(',') <= 4
self.do_run(src, '123\n123', post_build=(None, post))
+
+ def test_dlfcn_unique_sig(self):
+ if not self.can_dlfcn(): return
+
+ self.prep_dlfcn_lib()
+ lib_src = '''
+ #include <stdio.h>
+
+ int myfunc(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m) {
+ return 13;
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_myfunc']
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.c')
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ self.prep_dlfcn_main()
+ src = '''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <dlfcn.h>
+
+ typedef int (*FUNCTYPE)(int, int, int, int, int, int, int, int, int, int, int, int, int);
+
+ int main() {
+ void *lib_handle;
+ FUNCTYPE func_ptr;
+
+ lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle != NULL);
+
+ func_ptr = (FUNCTYPE)dlsym(lib_handle, "myfunc");
+ assert(func_ptr != NULL);
+ assert(func_ptr(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) == 13);
+
+ puts("success");
+
+ return 0;
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc']
+ self.do_run(src, 'success', force_c=True, post_build=self.dlfcn_post_build)
+
+ def test_dlfcn_stacks(self):
+ if not self.can_dlfcn(): return
+
+ self.prep_dlfcn_lib()
+ lib_src = '''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <string.h>
+
+ int myfunc(const char *input) {
+ char bigstack[1024] = { 0 };
+
+ // make sure we didn't just trample the stack!
+ assert(!strcmp(input, "foobar"));
+
+ snprintf(bigstack, sizeof(bigstack), input);
+ return strlen(bigstack);
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_myfunc']
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.c')
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ self.prep_dlfcn_main()
+ src = '''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <dlfcn.h>
+
+ typedef int (*FUNCTYPE)(const char *);
+
+ int main() {
+ void *lib_handle;
+ FUNCTYPE func_ptr;
+ char str[128];
+
+ snprintf(str, sizeof(str), "foobar");
+
+ lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle != NULL);
+
+ func_ptr = (FUNCTYPE)dlsym(lib_handle, "myfunc");
+ assert(func_ptr != NULL);
+ assert(func_ptr(str) == 6);
+
+ puts("success");
+
+ return 0;
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc']
+ self.do_run(src, 'success', force_c=True, post_build=self.dlfcn_post_build)
+
+ def test_dlfcn_funcs(self):
+ if not self.can_dlfcn(): return
+
+ self.prep_dlfcn_lib()
+ lib_src = r'''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <string.h>
+
+ typedef void (*voidfunc)();
+ typedef void (*intfunc)(int);
+
+ void callvoid(voidfunc f) { f(); }
+ void callint(voidfunc f, int x) { f(x); }
+
+ void void_0() { printf("void 0\n"); }
+ void void_1() { printf("void 1\n"); }
+ voidfunc getvoid(int i) {
+ switch(i) {
+ case 0: return void_0;
+ case 1: return void_1;
+ default: return NULL;
+ }
+ }
+
+ void int_0(int x) { printf("int 0 %d\n", x); }
+ void int_1(int x) { printf("int 1 %d\n", x); }
+ intfunc getint(int i) {
+ switch(i) {
+ case 0: return int_0;
+ case 1: return int_1;
+ default: return NULL;
+ }
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_callvoid', '_callint', '_getvoid', '_getint']
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.c')
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ self.prep_dlfcn_main()
+ src = r'''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <dlfcn.h>
+
+ typedef void (*voidfunc)();
+ typedef void (*intfunc)(int);
+
+ typedef void (*voidcaller)(voidfunc);
+ typedef void (*intcaller)(intfunc, int);
+
+ typedef voidfunc (*voidgetter)(int);
+ typedef intfunc (*intgetter)(int);
+
+ void void_main() { printf("main.\n"); }
+ void int_main(int x) { printf("main %d\n", x); }
+
+ int main() {
+ printf("go\n");
+ void *lib_handle;
+ lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle != NULL);
+
+ voidcaller callvoid = (voidcaller)dlsym(lib_handle, "callvoid");
+ assert(callvoid != NULL);
+ callvoid(void_main);
+
+ intcaller callint = (intcaller)dlsym(lib_handle, "callint");
+ assert(callint != NULL);
+ callint(int_main, 201);
+
+ voidgetter getvoid = (voidgetter)dlsym(lib_handle, "getvoid");
+ assert(getvoid != NULL);
+ callvoid(getvoid(0));
+ callvoid(getvoid(1));
+
+ intgetter getint = (intgetter)dlsym(lib_handle, "getint");
+ assert(getint != NULL);
+ callint(getint(0), 54);
+ callint(getint(1), 9000);
+
+ assert(getint(1000) == NULL);
+
+ puts("ok");
+ return 0;
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc']
+ self.do_run(src, '''go
+main.
+main 201
+void 0
+void 1
+int 0 54
+int 1 9000
+ok
+''', force_c=True, post_build=self.dlfcn_post_build)
+
+ def test_dlfcn_mallocs(self):
+ if not Settings.ASM_JS: return self.skip('needs asm')
+
+ if not self.can_dlfcn(): return
+
+ Settings.TOTAL_MEMORY = 64*1024*1024 # will be exhausted without functional malloc/free
+
+ self.prep_dlfcn_lib()
+ lib_src = r'''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+
+ void *mallocproxy(int n) { return malloc(n); }
+ void freeproxy(void *p) { free(p); }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_mallocproxy', '_freeproxy']
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.c')
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ self.prep_dlfcn_main()
+ src = open(path_from_root('tests', 'dlmalloc_proxy.c')).read()
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free']
+ self.do_run(src, '''*294,153*''', force_c=True, post_build=self.dlfcn_post_build)
+
def test_rand(self):
return self.skip('rand() is now random') # FIXME
@@ -7428,168 +7669,6 @@ def process(filename):
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
- def test_inet(self):
- src = r'''
- #include <stdio.h>
- #include <arpa/inet.h>
-
- int main() {
- printf("*%x,%x,%x,%x,%x,%x*\n", htonl(0xa1b2c3d4), htonl(0xfe3572e0), htonl(0x07abcdf0), htons(0xabcd), ntohl(0x43211234), ntohs(0xbeaf));
- in_addr_t i = inet_addr("190.180.10.78");
- printf("%x\n", i);
- return 0;
- }
- '''
- self.do_run(src, '*d4c3b2a1,e07235fe,f0cdab07,cdab,34122143,afbe*\n4e0ab4be\n')
-
- def test_inet2(self):
- src = r'''
- #include <stdio.h>
- #include <arpa/inet.h>
-
- int main() {
- struct in_addr x, x2;
- int *y = (int*)&x;
- *y = 0x12345678;
- printf("%s\n", inet_ntoa(x));
- int r = inet_aton(inet_ntoa(x), &x2);
- printf("%s\n", inet_ntoa(x2));
- return 0;
- }
- '''
- self.do_run(src, '120.86.52.18\n120.86.52.18\n')
-
- def test_inet3(self):
- src = r'''
- #include <stdio.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- int main() {
- char dst[64];
- struct in_addr x, x2;
- int *y = (int*)&x;
- *y = 0x12345678;
- printf("%s\n", inet_ntop(AF_INET,&x,dst,sizeof dst));
- int r = inet_aton(inet_ntoa(x), &x2);
- printf("%s\n", inet_ntop(AF_INET,&x2,dst,sizeof dst));
- return 0;
- }
- '''
- self.do_run(src, '120.86.52.18\n120.86.52.18\n')
-
- def test_inet4(self):
- if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
-
- src = r'''
- #include <stdio.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
-
- void test(char *test_addr){
- char str[40];
- struct in6_addr addr;
- unsigned char *p = (unsigned char*)&addr;
- int ret;
- ret = inet_pton(AF_INET6,test_addr,&addr);
- if(ret == -1) return;
- if(ret == 0) return;
- if(inet_ntop(AF_INET6,&addr,str,sizeof(str)) == NULL ) return;
- printf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x - %s\n",
- p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15],str);
- }
- int main(){
- test("::");
- test("::1");
- test("::1.2.3.4");
- test("::17.18.19.20");
- test("::ffff:1.2.3.4");
- test("1::ffff");
- test("::255.255.255.255");
- test("0:ff00:1::");
- test("0:ff::");
- test("abcd::");
- test("ffff::a");
- test("ffff::a:b");
- test("ffff::a:b:c");
- test("ffff::a:b:c:d");
- test("ffff::a:b:c:d:e");
- test("::1:2:0:0:0");
- test("0:0:1:2:3::");
- test("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
- test("1::255.255.255.255");
-
- //below should fail and not produce results..
- test("1.2.3.4");
- test("");
- test("-");
- }
- '''
- self.do_run(src,
- "0000:0000:0000:0000:0000:0000:0000:0000 - ::\n"
- "0000:0000:0000:0000:0000:0000:0000:0001 - ::1\n"
- "0000:0000:0000:0000:0000:0000:0102:0304 - ::1.2.3.4\n"
- "0000:0000:0000:0000:0000:0000:1112:1314 - ::17.18.19.20\n"
- "0000:0000:0000:0000:0000:ffff:0102:0304 - ::ffff:1.2.3.4\n"
- "0001:0000:0000:0000:0000:0000:0000:ffff - 1::ffff\n"
- "0000:0000:0000:0000:0000:0000:ffff:ffff - ::255.255.255.255\n"
- "0000:ff00:0001:0000:0000:0000:0000:0000 - 0:ff00:1::\n"
- "0000:00ff:0000:0000:0000:0000:0000:0000 - 0:ff::\n"
- "abcd:0000:0000:0000:0000:0000:0000:0000 - abcd::\n"
- "ffff:0000:0000:0000:0000:0000:0000:000a - ffff::a\n"
- "ffff:0000:0000:0000:0000:0000:000a:000b - ffff::a:b\n"
- "ffff:0000:0000:0000:0000:000a:000b:000c - ffff::a:b:c\n"
- "ffff:0000:0000:0000:000a:000b:000c:000d - ffff::a:b:c:d\n"
- "ffff:0000:0000:000a:000b:000c:000d:000e - ffff::a:b:c:d:e\n"
- "0000:0000:0000:0001:0002:0000:0000:0000 - ::1:2:0:0:0\n"
- "0000:0000:0001:0002:0003:0000:0000:0000 - 0:0:1:2:3::\n"
- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n"
- "0001:0000:0000:0000:0000:0000:ffff:ffff - 1::ffff:ffff\n"
- )
-
- def test_gethostbyname(self):
- if Settings.USE_TYPED_ARRAYS != 2: return self.skip("assume t2 in gethostbyname")
-
- src = r'''
- #include <netdb.h>
- #include <stdio.h>
-
- void test(char *hostname) {
- hostent *host = gethostbyname(hostname);
- if (!host) {
- printf("no such thing\n");
- return;
- }
- printf("%s : %d : %d\n", host->h_name, host->h_addrtype, host->h_length);
- char **name = host->h_aliases;
- while (*name) {
- printf("- %s\n", *name);
- name++;
- }
- name = host->h_addr_list;
- while (name && *name) {
- printf("* ");
- for (int i = 0; i < host->h_length; i++)
- printf("%d.", (*name)[i]);
- printf("\n");
- name++;
- }
- }
-
- int main() {
- test("www.cheezburger.com");
- test("fail.on.this.never.work"); // we will "work" on this - because we are just making aliases of names to ips
- test("localhost");
- return 0;
- }
- '''
- self.do_run(src, '''www.cheezburger.com : 2 : 4
-* -84.29.1.0.
-fail.on.this.never.work : 2 : 4
-* -84.29.2.0.
-localhost : 2 : 4
-* -84.29.3.0.
-''')
-
def test_799(self):
src = open(path_from_root('tests', '799.cpp'), 'r').read()
self.do_run(src, '''Set PORT family: 0, port: 3979
diff --git a/tests/test_other.py b/tests/test_other.py
index a6813b07..c6f5c333 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -929,20 +929,14 @@ f.close()
self.assertContained('libf1\nlibf2\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
def test_stdin(self):
- open('main.cpp', 'w').write(r'''
-#include <stdio.h>
-int main(int argc, char const *argv[])
-{
- char str[10] = {0};
- scanf("%10s", str);
- printf("%s\n", str);
- return 0;
-}
-''')
- Building.emcc('main.cpp', output_filename='a.out.js')
- open('in.txt', 'w').write('abc')
- # node's stdin support is broken
- self.assertContained('abc', Popen(listify(SPIDERMONKEY_ENGINE) + ['a.out.js'], stdin=open('in.txt'), stdout=PIPE, stderr=PIPE).communicate()[0])
+ Building.emcc(path_from_root('tests', 'module', 'test_stdin.c'), output_filename='a.out.js')
+ open('in.txt', 'w').write('abcdef\nghijkl')
+
+ for engine in JS_ENGINES:
+ print >> sys.stderr, engine
+ if engine == NODE_JS: continue # FIXME
+ if engine == V8_ENGINE: continue # no stdin support in v8 shell
+ self.assertContained('abcdef\nghijkl\neof', run_js(os.path.join(self.get_dir(), 'a.out.js'), engine=engine, stdin=open('in.txt')))
def test_ungetc_fscanf(self):
open('main.cpp', 'w').write(r'''
@@ -1890,3 +1884,12 @@ you should see two lines of text in different colors and a blue rectangle
SDL_Quit called (and ignored)
done.
''' in output, output
+
+ def test_preprocess(self):
+ self.clear()
+
+ out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-E'], stdout=PIPE).communicate()
+ assert not os.path.exists('a.out.js')
+ assert '''tests/hello_world.c"''' in out
+ assert '''printf("hello, world!''' in out
+
diff --git a/tests/test_sockets.py b/tests/test_sockets.py
index 82ddc6fe..d2bc46a2 100644
--- a/tests/test_sockets.py
+++ b/tests/test_sockets.py
@@ -34,11 +34,11 @@ def make_relay_server(port1, port2):
return proc
class WebsockifyServerHarness:
- def __init__(self, filename, args, listen_port, target_port):
+ def __init__(self, filename, args, listen_port):
self.pids = []
self.filename = filename
- self.target_port = target_port
self.listen_port = listen_port
+ self.target_port = listen_port-1
self.args = args or []
def __enter__(self):
@@ -48,7 +48,7 @@ class WebsockifyServerHarness:
# NOTE empty filename support is a hack to support
# the current test_enet
if self.filename:
- Popen([CLANG_CC, path_from_root('tests', self.filename), '-o', 'server'] + self.args).communicate()
+ Popen([CLANG_CC, path_from_root('tests', self.filename), '-o', 'server', '-DSOCKK=%d' % self.target_port] + self.args).communicate()
process = Popen([os.path.abspath('server')])
self.pids.append(process.pid)
@@ -71,17 +71,22 @@ class WebsockifyServerHarness:
class CompiledServerHarness:
- def __init__(self, filename, args):
+ def __init__(self, filename, args, listen_port):
self.pids = []
self.filename = filename
+ self.listen_port = listen_port
self.args = args or []
def __enter__(self):
- import socket, websockify
+ # assuming this is only used for WebSocket tests at the moment, validate that
+ # the ws module is installed
+ child = Popen(listify(NODE_JS) + ['-e', 'require("ws");'])
+ child.communicate()
+ assert child.returncode == 0, 'ws module for Node.js not installed. Please run \'npm install\' from %s' % EMSCRIPTEN_ROOT
# compile the server
- Popen([PYTHON, EMCC, path_from_root('tests', self.filename), '-o', 'server.js'] + self.args).communicate()
- process = Popen([NODE_JS, 'server.js'])
+ Popen([PYTHON, EMCC, path_from_root('tests', self.filename), '-o', 'server.js', '-DSOCKK=%d' % self.listen_port] + self.args).communicate()
+ process = Popen(listify(NODE_JS) + ['server.js'])
self.pids.append(process.pid)
def __exit__(self, *args, **kwargs):
@@ -96,72 +101,196 @@ class CompiledServerHarness:
# proper listen server support.
class sockets(BrowserCore):
+ def test_inet(self):
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+
+ int main() {
+ printf("*%x,%x,%x,%x,%x,%x*\n", htonl(0xa1b2c3d4), htonl(0xfe3572e0), htonl(0x07abcdf0), htons(0xabcd), ntohl(0x43211234), ntohs(0xbeaf));
+ in_addr_t i = inet_addr("190.180.10.78");
+ printf("%x\n", i);
+ return 0;
+ }
+ '''
+ self.do_run(src, '*d4c3b2a1,e07235fe,f0cdab07,cdab,34122143,afbe*\n4e0ab4be\n')
+
+ def test_inet2(self):
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+
+ int main() {
+ struct in_addr x, x2;
+ int *y = (int*)&x;
+ *y = 0x12345678;
+ printf("%s\n", inet_ntoa(x));
+ int r = inet_aton(inet_ntoa(x), &x2);
+ printf("%s\n", inet_ntoa(x2));
+ return 0;
+ }
+ '''
+ self.do_run(src, '120.86.52.18\n120.86.52.18\n')
+
+ def test_inet3(self):
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
+ int main() {
+ char dst[64];
+ struct in_addr x, x2;
+ int *y = (int*)&x;
+ *y = 0x12345678;
+ printf("%s\n", inet_ntop(AF_INET,&x,dst,sizeof dst));
+ int r = inet_aton(inet_ntoa(x), &x2);
+ printf("%s\n", inet_ntop(AF_INET,&x2,dst,sizeof dst));
+ return 0;
+ }
+ '''
+ self.do_run(src, '120.86.52.18\n120.86.52.18\n')
+
+ def test_inet4(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
+
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
+
+ void test(char *test_addr){
+ char str[40];
+ struct in6_addr addr;
+ unsigned char *p = (unsigned char*)&addr;
+ int ret;
+ ret = inet_pton(AF_INET6,test_addr,&addr);
+ if(ret == -1) return;
+ if(ret == 0) return;
+ if(inet_ntop(AF_INET6,&addr,str,sizeof(str)) == NULL ) return;
+ printf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x - %s\n",
+ p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15],str);
+ }
+ int main(){
+ test("::");
+ test("::1");
+ test("::1.2.3.4");
+ test("::17.18.19.20");
+ test("::ffff:1.2.3.4");
+ test("1::ffff");
+ test("::255.255.255.255");
+ test("0:ff00:1::");
+ test("0:ff::");
+ test("abcd::");
+ test("ffff::a");
+ test("ffff::a:b");
+ test("ffff::a:b:c");
+ test("ffff::a:b:c:d");
+ test("ffff::a:b:c:d:e");
+ test("::1:2:0:0:0");
+ test("0:0:1:2:3::");
+ test("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ test("1::255.255.255.255");
+
+ //below should fail and not produce results..
+ test("1.2.3.4");
+ test("");
+ test("-");
+ }
+ '''
+ self.do_run(src,
+ "0000:0000:0000:0000:0000:0000:0000:0000 - ::\n"
+ "0000:0000:0000:0000:0000:0000:0000:0001 - ::1\n"
+ "0000:0000:0000:0000:0000:0000:0102:0304 - ::1.2.3.4\n"
+ "0000:0000:0000:0000:0000:0000:1112:1314 - ::17.18.19.20\n"
+ "0000:0000:0000:0000:0000:ffff:0102:0304 - ::ffff:1.2.3.4\n"
+ "0001:0000:0000:0000:0000:0000:0000:ffff - 1::ffff\n"
+ "0000:0000:0000:0000:0000:0000:ffff:ffff - ::255.255.255.255\n"
+ "0000:ff00:0001:0000:0000:0000:0000:0000 - 0:ff00:1::\n"
+ "0000:00ff:0000:0000:0000:0000:0000:0000 - 0:ff::\n"
+ "abcd:0000:0000:0000:0000:0000:0000:0000 - abcd::\n"
+ "ffff:0000:0000:0000:0000:0000:0000:000a - ffff::a\n"
+ "ffff:0000:0000:0000:0000:0000:000a:000b - ffff::a:b\n"
+ "ffff:0000:0000:0000:0000:000a:000b:000c - ffff::a:b:c\n"
+ "ffff:0000:0000:0000:000a:000b:000c:000d - ffff::a:b:c:d\n"
+ "ffff:0000:0000:000a:000b:000c:000d:000e - ffff::a:b:c:d:e\n"
+ "0000:0000:0000:0001:0002:0000:0000:0000 - ::1:2:0:0:0\n"
+ "0000:0000:0001:0002:0003:0000:0000:0000 - 0:0:1:2:3::\n"
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n"
+ "0001:0000:0000:0000:0000:0000:ffff:ffff - 1::ffff:ffff\n"
+ )
+
+ def test_getaddrinfo(self):
+ self.do_run(open(path_from_root('tests', 'sockets', 'test_getaddrinfo.c')).read(), 'success')
+
+ def test_getnameinfo(self):
+ self.do_run(open(path_from_root('tests', 'sockets', 'test_getnameinfo.c')).read(), 'success')
+
+ def test_gethostbyname(self):
+ self.do_run(open(path_from_root('tests', 'sockets', 'test_gethostbyname.c')).read(), 'success')
+
def test_sockets_echo(self):
sockets_include = '-I'+path_from_root('tests', 'sockets')
- for datagram in [0]:
- dgram_define = '-DTEST_DGRAM=%d' % datagram
+ # Websockify-proxied servers can't run dgram tests
+ harnesses = [
+ (WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 49160), 0),
+ (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=0'], 49161), 0),
+ (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=1'], 49162), 1)
+ ]
- for harness in [
- WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8990', dgram_define, sockets_include], 8991, 8990)
- # CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8990', dgram_define, sockets_include])
- ]:
- with harness:
- self.btest(os.path.join('sockets', 'test_sockets_echo_client.c'), expected='0', args=['-DSOCKK=8991', dgram_define, sockets_include])
+ for harness, datagram in harnesses:
+ with harness:
+ self.btest(os.path.join('sockets', 'test_sockets_echo_client.c'), expected='0', args=['-DSOCKK=%d' % harness.listen_port, '-DTEST_DGRAM=%d' % datagram, sockets_include])
def test_sockets_echo_bigdata(self):
sockets_include = '-I'+path_from_root('tests', 'sockets')
- for datagram in [0]:
- dgram_define = '-DTEST_DGRAM=%d' % datagram
+ # generate a large string literal to use as our message
+ message = ''
+ for i in range(256*256*2):
+ message += str(unichr(ord('a') + (i % 26)))
- # generate a large string literal to use as our message
- message = ''
- for i in range(256*256*2):
- message += str(unichr(ord('a') + (i % 26)))
+ # re-write the client test with this literal (it's too big to pass via command line)
+ input_filename = path_from_root('tests', 'sockets', 'test_sockets_echo_client.c')
+ input = open(input_filename).read()
+ output = input.replace('#define MESSAGE "pingtothepong"', '#define MESSAGE "%s"' % message)
- # re-write the client test with this literal (it's too big to pass via command line)
- input_filename = path_from_root('tests', 'sockets', 'test_sockets_echo_client.c')
- input = open(input_filename).read()
- output = input.replace('#define MESSAGE "pingtothepong"', '#define MESSAGE "%s"' % message)
+ harnesses = [
+ (WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 49170), 0),
+ (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=0'], 49171), 0),
+ (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=1'], 49172), 1)
+ ]
- for harness in [
- WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8992', dgram_define, sockets_include], 8993, 8992)
- ]:
- with harness:
- self.btest(output, expected='0', args=['-DSOCKK=8993', dgram_define, sockets_include], force_c=True)
+ for harness, datagram in harnesses:
+ with harness:
+ self.btest(output, expected='0', args=[sockets_include, '-DSOCKK=%d' % harness.listen_port, '-DTEST_DGRAM=%d' % datagram], force_c=True)
def test_sockets_partial(self):
for harness in [
- WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_partial_server.c'), ['-DSOCKK=8994'], 8995, 8994)
+ WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_partial_server.c'), [], 49180),
+ CompiledServerHarness(os.path.join('sockets', 'test_sockets_partial_server.c'), [], 49181)
]:
with harness:
- self.btest(os.path.join('sockets', 'test_sockets_partial_client.c'), expected='165', args=['-DSOCKK=8995'])
-
- # TODO add support for gethostbyaddr to re-enable this test
- # def test_sockets_gethostbyname(self):
- # self.btest(os.path.join('sockets', 'test_sockets_gethostbyname.c'), expected='0', args=['-O2', '-DSOCKK=8997'])
+ self.btest(os.path.join('sockets', 'test_sockets_partial_client.c'), expected='165', args=['-DSOCKK=%d' % harness.listen_port])
def test_sockets_select_server_down(self):
for harness in [
- WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_select_server_down_server.c'), ['-DSOCKK=9002'], 9003, 9002)
+ WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_select_server_down_server.c'), [], 49190),
+ CompiledServerHarness(os.path.join('sockets', 'test_sockets_select_server_down_server.c'), [], 49191)
]:
with harness:
- self.btest(os.path.join('sockets', 'test_sockets_select_server_down_client.c'), expected='266', args=['-DSOCKK=9003'])
+ self.btest(os.path.join('sockets', 'test_sockets_select_server_down_client.c'), expected='266', args=['-DSOCKK=%d' % harness.listen_port])
def test_sockets_select_server_closes_connection_rw(self):
sockets_include = '-I'+path_from_root('tests', 'sockets')
for harness in [
- WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=9004', sockets_include], 9005, 9004)
+ WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DCLOSE_CLIENT_AFTER_ECHO'], 49200),
+ CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DCLOSE_CLIENT_AFTER_ECHO'], 49201)
]:
with harness:
- self.btest(os.path.join('sockets', 'test_sockets_select_server_closes_connection_client_rw.c'), expected='266', args=['-DSOCKK=9005', sockets_include])
+ self.btest(os.path.join('sockets', 'test_sockets_select_server_closes_connection_client_rw.c'), expected='266', args=[sockets_include, '-DSOCKK=%d' % harness.listen_port])
- # TODO remove this once we have proper listen server support built into emscripten.
- # being that enet uses datagram sockets, we can't proxy to a native server with
- # websockify, so we're emulating the listen server in the browser and relaying
- # between two TCP servers.
def test_enet(self):
try_delete(self.in_dir('enet'))
shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet'))
@@ -171,20 +300,18 @@ class sockets(BrowserCore):
Popen([PYTHON, path_from_root('emmake'), 'make']).communicate()
enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')]
os.chdir(pwd)
- Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_enet_server.c'), '-o', 'server.html', '-DSOCKK=2235'] + enet).communicate()
-
- with WebsockifyServerHarness('', [], 2235, 2234):
- with WebsockifyServerHarness('', [], 2237, 2236):
- pids = []
- try:
- proc = make_relay_server(2234, 2236)
- pids.append(proc.pid)
- self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=2237', '-DUSE_IFRAME'] + enet)
- finally:
- clean_pids(pids);
-
- # TODO use this once we have listen server support
- # def test_enet(self):
+
+ for harness in [
+ CompiledServerHarness(os.path.join('sockets', 'test_enet_server.c'), enet, 49210)
+ ]:
+ with harness:
+ self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=enet + ['-DSOCKK=%d' % harness.listen_port])
+
+ # This test is no longer in use for WebSockets as we can't truly emulate
+ # a server in the browser (in the past, there were some hacks to make it
+ # somewhat work, but those have been removed). However, with WebRTC it
+ # should be able to resurect this test.
+ # def test_enet_in_browser(self):
# try_delete(self.in_dir('enet'))
# shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet'))
# pwd = os.getcwd()
@@ -193,12 +320,17 @@ class sockets(BrowserCore):
# Popen([PYTHON, path_from_root('emmake'), 'make']).communicate()
# enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')]
# os.chdir(pwd)
-
- # for harness in [
- # self.CompiledServerHarness(os.path.join('sockets', 'test_enet_server.c'), ['-DSOCKK=9010'] + enet, 9011, 9010)
- # ]:
- # with harness:
- # self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=9011'] + enet)
+ # Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_enet_server.c'), '-o', 'server.html', '-DSOCKK=2235'] + enet).communicate()
+
+ # with WebsockifyServerHarness('', [], 2235, 2234):
+ # with WebsockifyServerHarness('', [], 2237, 2236):
+ # pids = []
+ # try:
+ # proc = make_relay_server(2234, 2236)
+ # pids.append(proc.pid)
+ # self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=2237', '-DUSE_IFRAME=1'] + enet)
+ # finally:
+ # clean_pids(pids);
def test_webrtc(self):
host_src = 'webrtc_host.c'
@@ -266,4 +398,5 @@ class sockets(BrowserCore):
Popen([PYTHON, EMCC, temp_peer_filepath, '-o', peer_outfile] + ['-s', 'GL_TESTING=1', '--pre-js', 'peer_pre.js', '-s', 'SOCKET_WEBRTC=1', '-s', 'SOCKET_DEBUG=1']).communicate()
expected = '1'
- self.run_browser(host_outfile, '.', ['/report_result?' + e for e in expected]) \ No newline at end of file
+ self.run_browser(host_outfile, '.', ['/report_result?' + e for e in expected])
+
diff --git a/tools/jsrun.py b/tools/jsrun.py
index 571e9cee..6f77ce51 100644
--- a/tools/jsrun.py
+++ b/tools/jsrun.py
@@ -10,15 +10,17 @@ def timeout_run(proc, timeout, note='unnamed process', full_output=False):
proc.kill() # XXX bug: killing emscripten.py does not kill it's child process!
raise Exception("Timed out: " + note)
out = proc.communicate()
+ out = map(lambda o: '' if o is None else o, out)
return '\n'.join(out) if full_output else out[0]
-def run_js(filename, engine=None, args=[], check_timeout=False, stdout=PIPE, stderr=None, cwd=None, full_output=False):
+def run_js(filename, engine=None, args=[], check_timeout=False, stdin=None, stdout=PIPE, stderr=None, cwd=None, full_output=False):
if type(engine) is not list:
engine = [engine]
command = engine + [filename] + (['--'] if 'd8' in engine[0] or 'jsc' in engine[0] else []) + args
return timeout_run(
Popen(
command,
+ stdin=stdin,
stdout=stdout,
stderr=stderr,
cwd=cwd),
diff --git a/tools/shared.py b/tools/shared.py
index 3ee5db23..b0b3985e 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -717,6 +717,10 @@ class Settings2(type):
return ret
@classmethod
+ def copy(self, values):
+ self.attrs = values
+
+ @classmethod
def apply_opt_level(self, opt_level, noisy=False):
if opt_level >= 1:
self.attrs['ASM_JS'] = 1
@@ -843,6 +847,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
raise
del env['EMMAKEN_JUST_CONFIGURE']
if process.returncode is not 0:
+ logging.error('Configure step failed with non-zero return code ' + str(process.returncode) + '! Command line: ' + str(args))
raise subprocess.CalledProcessError(cmd=args, returncode=process.returncode)
@staticmethod
@@ -893,12 +898,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
# except:
# pass
env = Building.get_building_env(native)
+ log_to_file = os.getenv('EM_BUILD_VERBOSE') == None or int(os.getenv('EM_BUILD_VERBOSE')) == 0
for k, v in env_init.iteritems():
env[k] = v
if configure: # Useful in debugging sometimes to comment this out (and the lines below up to and including the |link| call)
try:
- Building.configure(configure + configure_args, stdout=open(os.path.join(project_dir, 'configure_'), 'w'),
- stderr=open(os.path.join(project_dir, 'configure_err'), 'w'), env=env)
+ Building.configure(configure + configure_args, env=env, stdout=open(os.path.join(project_dir, 'configure_'), 'w') if log_to_file else None,
+ stderr=open(os.path.join(project_dir, 'configure_err'), 'w') if log_to_file else None)
except subprocess.CalledProcessError, e:
pass # Ignore exit code != 0
def open_make_out(i, mode='r'):
@@ -911,8 +917,8 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
with open_make_out(i, 'w') as make_out:
with open_make_err(i, 'w') as make_err:
try:
- Building.make(make + make_args, stdout=make_out,
- stderr=make_err, env=env)
+ Building.make(make + make_args, stdout=make_out if log_to_file else None,
+ stderr=make_err if log_to_file else None, env=env)
except subprocess.CalledProcessError, e:
pass # Ignore exit code != 0
try:
@@ -924,10 +930,11 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
break
except Exception, e:
if i > 0:
- # Due to the ugly hack above our best guess is to output the first run
- with open_make_err(0) as ferr:
- for line in ferr:
- sys.stderr.write(line)
+ if log_to_file:
+ # Due to the ugly hack above our best guess is to output the first run
+ with open_make_err(0) as ferr:
+ for line in ferr:
+ sys.stderr.write(line)
raise Exception('could not build library ' + name + ' due to exception ' + str(e))
if old_dir:
os.chdir(old_dir)
@@ -1176,8 +1183,6 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
def get_safe_internalize():
if not Building.can_build_standalone(): return [] # do not internalize anything
exps = expand_response(Settings.EXPORTED_FUNCTIONS)
- if '_malloc' not in exps: exps.append('_malloc') # needed internally, even if user did not add to EXPORTED_FUNCTIONS
- if '_free' not in exps: exps.append('_free')
exports = ','.join(map(lambda exp: exp[1:], exps))
# internalize carefully, llvm 3.2 will remove even main if not told not to
return ['-internalize', '-internalize-public-api-list=' + exports]
@@ -1426,7 +1431,7 @@ JCache = cache.JCache(Cache)
chunkify = cache.chunkify
class JS:
- memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASE+]+)\)'
+ memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASEH+]+)\)'
no_memory_initializer_pattern = '/\* no memory initializer \*/'
memory_staticbump_pattern = 'STATICTOP = STATIC_BASE \+ (\d+);'
@@ -1438,11 +1443,32 @@ class JS:
return ident.replace('%', '$').replace('@', '_')
@staticmethod
+ def make_extcall(sig, named=True):
+ args = ','.join(['a' + str(i) for i in range(1, len(sig))])
+ args = 'index' + (',' if args else '') + args
+ # C++ exceptions are numbers, and longjmp is a string 'longjmp'
+ ret = '''function%s(%s) {
+ %sModule["dynCall_%s"](%s);
+}''' % ((' extCall_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args)
+
+ if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS:
+ # guard against cross-module stack leaks
+ ret = ret.replace(') {\n', ''') {
+ try {
+ var preStack = asm.stackSave();
+''').replace(';\n}', ''';
+ } finally {
+ assert(asm.stackSave() == preStack);
+ }
+}''')
+ return ret
+
+ @staticmethod
def make_invoke(sig, named=True):
args = ','.join(['a' + str(i) for i in range(1, len(sig))])
args = 'index' + (',' if args else '') + args
# C++ exceptions are numbers, and longjmp is a string 'longjmp'
- return '''function%s(%s) {
+ ret = '''function%s(%s) {
try {
%sModule["dynCall_%s"](%s);
} catch(e) {
@@ -1451,6 +1477,17 @@ class JS:
}
}''' % ((' invoke_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args)
+ if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS:
+ # guard against cross-module stack leaks
+ ret = ret.replace(' try {', ''' var preStack = asm.stackSave();
+ try {
+''').replace(' }\n}', ''' } finally {
+ assert(asm.stackSave() == preStack);
+ }
+}''')
+
+ return ret
+
@staticmethod
def align(x, by):
while x % by != 0: x += 1