summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rwxr-xr-xemcc24
-rwxr-xr-xemlibtool11
-rw-r--r--emlibtool.bat2
-rwxr-xr-xemscripten.py114
-rw-r--r--package.json7
-rw-r--r--src/intertyper.js10
-rw-r--r--src/jsifier.js35
-rw-r--r--src/library.js1630
-rw-r--r--src/library_fs.js1399
-rw-r--r--src/library_gl.js15
-rw-r--r--src/library_openal.js17
-rw-r--r--src/library_sdl.js32
-rw-r--r--src/library_sockfs.js568
-rw-r--r--src/library_tty.js32
-rw-r--r--src/modules.js1
-rw-r--r--src/parseTools.js9
-rw-r--r--src/preamble.js4
-rw-r--r--src/settings.js40
-rw-r--r--src/shell_sharedlib.js19
-rw-r--r--system/include/libcxx/__locale12
-rw-r--r--system/include/libcxx/locale2
-rw-r--r--system/include/netdb.h1
-rw-r--r--system/lib/libcxx/exception.cpp6
-rw-r--r--system/lib/libcxx/locale.cpp24
-rw-r--r--system/lib/libcxxabi/src/cxa_new_delete.cpp2
-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/filesystem/src.js22
-rw-r--r--tests/gl_vertex_buffer.c195
-rw-r--r--tests/gl_vertex_buffer.pngbin0 -> 47668 bytes
-rw-r--r--tests/gl_vertex_buffer_pre.c177
-rw-r--r--tests/gl_vertex_buffer_pre.pngbin0 -> 83534 bytes
-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.py6
-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/test_browser.py11
-rw-r--r--tests/test_core.py781
-rw-r--r--tests/test_other.py9
-rw-r--r--tests/test_sockets.py263
-rw-r--r--tests/unistd/access.c8
-rw-r--r--tests/unistd/access.js4
-rw-r--r--tests/unistd/access.out4
-rw-r--r--tests/unistd/curdir.c11
-rw-r--r--tests/unistd/curdir.js7
-rw-r--r--tests/unistd/io.c50
-rw-r--r--tests/unistd/io.js52
-rw-r--r--tests/unistd/links.c7
-rw-r--r--tests/unistd/links.js3
-rw-r--r--tests/unistd/truncate.c7
-rw-r--r--tests/unistd/truncate.js2
-rw-r--r--tools/js-optimizer.js16
-rw-r--r--tools/jsrun.py1
-rw-r--r--tools/shared.py89
84 files changed, 4537 insertions, 1978 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..958ed7cd 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,24 @@ 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')
+
+ if shared.Settings.ASM_JS and shared.Settings.DLOPEN_SUPPORT:
+ assert shared.Settings.DISABLE_EXCEPTION_CATCHING, 'no exceptions support with dlopen in asm yet'
+
## Compile source code to bitcode
logging.debug('compiling to bitcode')
diff --git a/emlibtool b/emlibtool
deleted file mode 100755
index 1eb18edc..00000000
--- a/emlibtool
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env python2
-
-'''
-This is a helper script. See emcc.
-'''
-
-import os, sys
-from tools import shared
-
-raise Exception('TODO: emlibtool')
-
diff --git a/emlibtool.bat b/emlibtool.bat
deleted file mode 100644
index 4ea705be..00000000
--- a/emlibtool.bat
+++ /dev/null
@@ -1,2 +0,0 @@
-@echo off
-python "%~dp0\emlibtool" %* \ No newline at end of file
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/intertyper.js b/src/intertyper.js
index 31e97bd0..ddb93d71 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -707,16 +707,24 @@ function intertyper(data, sidePass, baseLineNums) {
var tokensLeft = item.tokens.slice(2);
item.ident = eatLLVMIdent(tokensLeft);
if (item.ident == 'asm') {
+ if (ASM_JS) {
+ warnOnce('inline JS in asm.js mode can cause the code to no longer fall in the asm.js subset of JavaScript');
+ }
+ assert(TARGET_LE32, 'inline js is only supported in le32');
// Inline assembly is just JavaScript that we paste into the code
item.intertype = 'value';
if (tokensLeft[0].text == 'sideeffect') tokensLeft.splice(0, 1);
item.ident = tokensLeft[0].text.substr(1, tokensLeft[0].text.length-2) || ';'; // use ; for empty inline assembly
var i = 0;
+ var params = [], args = [];
splitTokenList(tokensLeft[3].item.tokens).map(function(element) {
var ident = toNiceIdent(element[1].text);
var type = element[0].text;
- item.ident = item.ident.replace(new RegExp('\\$' + i++, 'g'), ident);
+ params.push('$' + (i++));
+ args.push(ident);
});
+ if (item.assignTo) item.ident = 'return ' + item.ident;
+ item.ident = '(function(' + params + ') { ' + item.ident + ' })(' + args + ');';
return { forward: null, ret: [item], item: item };
}
if (item.ident.substr(-2) == '()') {
diff --git a/src/jsifier.js b/src/jsifier.js
index 7273f54c..38f3bd5e 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -338,6 +338,7 @@ function JSify(data, functionsOnly, givenFunctions) {
// External variables in shared libraries should not be declared as
// they would shadow similarly-named globals in the parent, so do nothing here.
if (BUILD_AS_SHARED_LIB) return ret;
+ if (SIDE_MODULE) return [];
// Library items need us to emit something, but everything else requires nothing.
if (!LibraryManager.library[item.ident.slice(1)]) return ret;
}
@@ -1142,8 +1143,8 @@ function JSify(data, functionsOnly, givenFunctions) {
});
var range = maxx - minn;
var useIfs = (item.switchLabels.length+1) < 6 || range > 10*1024 || (range/item.switchLabels.length) > 1024; // heuristics
- if (VERBOSE && useIfs && item.switchLabels.length > 2) {
- warn('not optimizing llvm switch into js switch because ' + [range, range/item.switchLabels.length]);
+ if (VERBOSE && useIfs && item.switchLabels.length >= 6) {
+ warn('not optimizing llvm switch into js switch because range of values is ' + range + ', density is ' + range/item.switchLabels.length);
}
var phiSets = calcPhiSets(item);
@@ -1405,7 +1406,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 && !funcData.setjmpTable) extCall = true; // go out, to be able to access other modules TODO: optimize
ident = Variables.resolveAliasToIdent(ident);
var shortident = ident.slice(1);
@@ -1466,7 +1470,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 +1560,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 +1664,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 +1697,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 +1709,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 +1763,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 +1788,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 f6b3d5ef..f3c3c1ec 100644
--- a/src/library.js
+++ b/src/library.js
@@ -323,10 +323,16 @@ LibraryManager.library = {
path = Pointer_stringify(path);
// we don't want this in the JS API as the JS API
// uses mknod to create all nodes.
- var err = FS.mayMknod(mode);
- if (err) {
- ___setErrNo(err);
- return -1;
+ switch (mode & {{{ cDefine('S_IFMT') }}}) {
+ case {{{ cDefine('S_IFREG') }}}:
+ case {{{ cDefine('S_IFCHR') }}}:
+ case {{{ cDefine('S_IFBLK') }}}:
+ case {{{ cDefine('S_IFIFO') }}}:
+ case {{{ cDefine('S_IFSOCK') }}}:
+ break;
+ default:
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return -1;
}
try {
FS.mknod(path, mode, dev);
@@ -600,31 +606,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 +730,7 @@ LibraryManager.library = {
FS.close(stream);
return 0;
} catch (e) {
- FS.handleFSError(e);;
+ FS.handleFSError(e);
return -1;
}
},
@@ -1006,9 +1013,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 +1148,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) }}};
@@ -4268,9 +4279,11 @@ LibraryManager.library = {
__cxa_guard_release: function() {},
__cxa_guard_abort: function() {},
+#if USE_TYPED_ARRAYS != 2
_ZTVN10__cxxabiv119__pointer_type_infoE: [0], // is a pointer
_ZTVN10__cxxabiv117__class_type_infoE: [1], // no inherited classes
_ZTVN10__cxxabiv120__si_class_type_infoE: [2], // yes inherited classes
+#endif
// Exceptions
__cxa_allocate_exception: function(size) {
@@ -4652,20 +4665,28 @@ LibraryManager.library = {
cos: 'Math.cos',
cosf: 'Math.cos',
+ cosl: 'Math.cos',
sin: 'Math.sin',
sinf: 'Math.sin',
+ sinl: 'Math.sin',
tan: 'Math.tan',
tanf: 'Math.tan',
+ tanl: 'Math.tan',
acos: 'Math.acos',
acosf: 'Math.acos',
+ acosl: 'Math.acos',
asin: 'Math.asin',
asinf: 'Math.asin',
+ asinl: 'Math.asin',
atan: 'Math.atan',
atanf: 'Math.atan',
+ atanl: 'Math.atan',
atan2: 'Math.atan2',
atan2f: 'Math.atan2',
+ atan2l: 'Math.atan2',
exp: 'Math.exp',
expf: 'Math.exp',
+ expl: 'Math.exp',
// The erf and erfc functions are inspired from
// http://www.digitalmars.com/archives/cplusplus/3634.html
@@ -4701,7 +4722,8 @@ LibraryManager.library = {
} while (Math.abs(q1 - q2) / q2 > MATH_TOLERANCE);
return (ONE_SQRTPI * Math.exp(- x * x) * q2);
},
- erfcf: 'erfcf',
+ erfcf: 'erfc',
+ erfcl: 'erfc',
erf__deps: ['erfc'],
erf: function(x) {
var MATH_TOLERANCE = 1E-12;
@@ -4725,18 +4747,25 @@ LibraryManager.library = {
return (TWO_SQRTPI * sum);
},
erff: 'erf',
+ erfl: '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',
@@ -4807,6 +4836,7 @@ LibraryManager.library = {
return __reallyNegative(a) === __reallyNegative(b) ? a : -a;
},
copysignf: 'copysign',
+ copysignl: 'copysign',
__signbit__deps: ['copysign'],
__signbit: function(x) {
// We implement using copysign so that we get support
@@ -4819,56 +4849,70 @@ LibraryManager.library = {
return Math.sqrt(a*a + b*b);
},
hypotf: 'hypot',
+ hypotl: 'hypot',
sinh: function(x) {
var p = Math.pow(Math.E, x);
return (p - (1 / p)) / 2;
},
sinhf: 'sinh',
+ sinhl: 'sinh',
cosh: function(x) {
var p = Math.pow(Math.E, x);
return (p + (1 / p)) / 2;
},
coshf: 'cosh',
+ coshl: 'cosh',
tanh__deps: ['sinh', 'cosh'],
tanh: function(x) {
return _sinh(x) / _cosh(x);
},
tanhf: 'tanh',
+ tanhl: 'tanh',
asinh: function(x) {
return Math.log(x + Math.sqrt(x * x + 1));
},
asinhf: 'asinh',
+ asinhl: 'asinh',
acosh: function(x) {
return Math.log(x * 1 + Math.sqrt(x * x - 1));
},
acoshf: 'acosh',
+ acoshl: 'acosh',
atanh: function(x) {
return Math.log((1 + x) / (1 - x)) / 2;
},
atanhf: 'atanh',
+ atanhl: 'atanh',
exp2: function(x) {
return Math.pow(2, x);
},
exp2f: 'exp2',
+ exp2l: 'exp2',
expm1: function(x) {
return Math.exp(x) - 1;
},
expm1f: 'expm1',
+ expm1l: 'expm1',
round: function(x) {
return (x < 0) ? -Math.round(-x) : Math.round(x);
},
roundf: 'round',
+ roundl: 'round',
lround: 'round',
lroundf: 'round',
+ lroundl: 'round',
llround: 'round',
llroundf: 'round',
+ llroundl: 'round',
rint: function(x) {
if (Math.abs(x % 1) !== 0.5) return Math.round(x);
return x + x % 2 + ((x < 0) ? 1 : -1);
},
rintf: 'rint',
+ rintl: 'rint',
lrint: 'rint',
lrintf: 'rint',
+ lrintl: 'rint',
#if USE_TYPED_ARRAYS == 2
llrint: function(x) {
x = (x < 0) ? -Math.round(-x) : Math.round(x);
@@ -4878,50 +4922,63 @@ LibraryManager.library = {
llrint: 'rint',
#endif
llrintf: 'llrint',
+ llrintl: 'llrint',
nearbyint: 'rint',
nearbyintf: 'rint',
+ nearbyintl: 'rint',
trunc: function(x) {
return (x < 0) ? Math.ceil(x) : Math.floor(x);
},
truncf: 'trunc',
+ truncl: 'trunc',
fdim: function(x, y) {
return (x > y) ? x - y : 0;
},
fdimf: 'fdim',
+ fdiml: 'fdim',
fmax: function(x, y) {
return isNaN(x) ? y : isNaN(y) ? x : Math.max(x, y);
},
fmaxf: 'fmax',
+ fmaxl: 'fmax',
fmin: function(x, y) {
return isNaN(x) ? y : isNaN(y) ? x : Math.min(x, y);
},
fminf: 'fmin',
+ fminl: 'fmin',
fma: function(x, y, z) {
return x * y + z;
},
fmaf: 'fma',
+ fmal: 'fma',
fmod: function(x, y) {
return x % y;
},
fmodf: 'fmod',
+ fmodl: 'fmod',
remainder: 'fmod',
remainderf: 'fmod',
+ remainderl: 'fmod',
log10: function(x) {
return Math.log(x) / Math.LN10;
},
log10f: 'log10',
+ log10l: 'log10',
log1p: function(x) {
return Math.log(1 + x);
},
log1pf: 'log1p',
+ log1pl: 'log1p',
log2: function(x) {
return Math.log(x) / Math.LN2;
},
log2f: 'log2',
+ log2l: 'log2',
nan: function(x) {
return NaN;
},
nanf: 'nan',
+ nanl: 'nan',
sincos: function(x, sine, cosine) {
var sineVal = Math.sin(x),
@@ -4929,6 +4986,7 @@ LibraryManager.library = {
{{{ makeSetValue('sine', '0', 'sineVal', 'double') }}};
{{{ makeSetValue('cosine', '0', 'cosineVal', 'double') }}};
},
+ sincosl: 'sincos',
sincosf: function(x, sine, cosine) {
var sineVal = Math.sin(x),
@@ -5007,24 +5065,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;
}
@@ -5035,7 +5143,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);
@@ -5043,19 +5151,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.
@@ -5069,60 +5184,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;
@@ -5131,18 +5252,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;
}
},
@@ -5222,8 +5343,8 @@ LibraryManager.library = {
['i32', 'tm_zone']]),
// Statically allocated time struct.
__tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)',
- // Statically allocated timezone strings.
- __tm_timezones: {},
+ // Statically allocated timezone string. We only use GMT as a timezone.
+ __tm_timezone: 'allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)',
// Statically allocated time strings.
__tm_formatted: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)',
@@ -5251,7 +5372,7 @@ LibraryManager.library = {
return _gmtime_r(time, ___tm_current);
},
- gmtime_r__deps: ['__tm_struct_layout', '__tm_timezones'],
+ gmtime_r__deps: ['__tm_struct_layout', '__tm_timezone'],
gmtime_r: function(time, tmPtr) {
var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000);
var offsets = ___tm_struct_layout;
@@ -5273,12 +5394,7 @@ LibraryManager.library = {
start.setUTCMilliseconds(0);
var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
{{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}}
-
- var timezone = "GMT";
- if (!(timezone in ___tm_timezones)) {
- ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL);
- }
- {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezones[timezone]', 'i32') }}}
+ {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezone', 'i32') }}}
return tmPtr;
},
@@ -5297,7 +5413,7 @@ LibraryManager.library = {
return _localtime_r(time, ___tm_current);
},
- localtime_r__deps: ['__tm_struct_layout', '__tm_timezones', 'tzset'],
+ localtime_r__deps: ['__tm_struct_layout', '__tm_timezone', 'tzset'],
localtime_r: function(time, tmPtr) {
_tzset();
var offsets = ___tm_struct_layout;
@@ -5318,11 +5434,7 @@ LibraryManager.library = {
var dst = Number(start.getTimezoneOffset() != date.getTimezoneOffset());
{{{ makeSetValue('tmPtr', 'offsets.tm_isdst', 'dst', 'i32') }}}
- var timezone = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | date.toString().match(/\(([A-Z]+)\)/)[1];
- if (!(timezone in ___tm_timezones)) {
- ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL);
- }
- {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezones[timezone]', 'i32') }}}
+ {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezone', 'i32') }}}
return tmPtr;
},
@@ -6004,7 +6116,7 @@ LibraryManager.library = {
// int clock_gettime(clockid_t clk_id, struct timespec *tp);
var now = Date.now();
{{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', 'Math.floor(now/1000)', 'i32') }}}; // seconds
- {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}; // nanoseconds - not supported
+ {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '(now % 1000) * 1000 * 1000', 'i32') }}}; // nanoseconds (really milliseconds)
return 0;
},
clock_settime: function(clk_id, tp) {
@@ -6016,7 +6128,7 @@ LibraryManager.library = {
clock_getres: function(clk_id, res) {
// int clock_getres(clockid_t clk_id, struct timespec *res);
{{{ makeSetValue('res', '___timespec_struct_layout.tv_sec', '1', 'i32') }}}
- {{{ makeSetValue('res', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}
+ {{{ makeSetValue('res', '___timespec_struct_layout.tv_nsec', '1000 * 1000', 'i32') }}} // resolution is milliseconds
return 0;
},
@@ -6910,7 +7022,6 @@ LibraryManager.library = {
// ==========================================================================
// arpa/inet.h
// ==========================================================================
-
htonl: function(value) {
return ((value & 0xff) << 24) + ((value & 0xff00) << 8) +
((value & 0xff0000) >>> 8) + ((value & 0xff000000) >>> 24);
@@ -6921,75 +7032,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;
@@ -6997,9 +7076,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
@@ -7014,7 +7092,6 @@ LibraryManager.library = {
// +--------------------------------------+----+---------------------+
// |0000..............................0000|FFFF| IPv4 ADDRESS | (mapped)
// +--------------------------------------+----+---------------------+
-
var str = "";
var word = 0;
var longest = 0;
@@ -7022,27 +7099,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
@@ -7056,7 +7143,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;
}
@@ -7072,7 +7159,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
@@ -7081,54 +7168,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;
@@ -7137,22 +7256,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)',
@@ -7173,10 +7339,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'],
@@ -7186,44 +7398,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
@@ -7257,6 +7698,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'],
@@ -7266,6 +7714,10 @@ LibraryManager.library = {
['i32', 'msg_controllen'],
['i32', 'msg_flags'],
]),
+ iovec_layout: Runtime.generateStructInfo([
+ ['i8*', 'iov_base'],
+ ['i32', 'iov_len']
+ ])
},
#if SOCKET_WEBRTC
@@ -7424,7 +7876,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;
@@ -7436,7 +7888,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;
}
@@ -7445,7 +7897,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;
@@ -7459,7 +7911,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);
@@ -7650,425 +8102,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);
-
- var errorCondition = 0;
+ assert(nfds <= 64, 'nfds must be less than or equal to 64'); // fd sets have 64 bits
+ assert(!exceptfds, 'exceptfds not supported');
- 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_fs.js b/src/library_fs.js
index 1d9748d3..4a150d80 100644
--- a/src/library_fs.js
+++ b/src/library_fs.js
@@ -14,11 +14,10 @@ mergeInto(LibraryManager.library, {
'Module["FS_createDevice"] = FS.createDevice;',
$FS: {
root: null,
- nodes: [null],
devices: [null],
streams: [null],
nextInode: 1,
- name_table: null,
+ nameTable: null,
currentPath: '/',
initialized: false,
// Whether we are currently ignoring permissions. Useful when preparing the
@@ -49,6 +48,76 @@ mergeInto(LibraryManager.library, {
},
//
+ // paths
+ //
+ cwd: function() {
+ return FS.currentPath;
+ },
+ lookupPath: function(path, opts) {
+ path = PATH.resolve(FS.currentPath, path);
+ opts = opts || { recurse_count: 0 };
+
+ if (opts.recurse_count > 8) { // max recursive lookup of 8
+ throw new FS.ErrnoError(ERRNO_CODES.ELOOP);
+ }
+
+ // split the path
+ var parts = PATH.normalizeArray(path.split('/').filter(function(p) {
+ return !!p;
+ }), false);
+
+ // start at the root
+ var current = FS.root;
+ var current_path = '/';
+
+ for (var i = 0; i < parts.length; i++) {
+ var islast = (i === parts.length-1);
+ if (islast && opts.parent) {
+ // stop resolving
+ break;
+ }
+
+ current = FS.lookupNode(current, parts[i]);
+ current_path = PATH.join(current_path, parts[i]);
+
+ // jump to the mount's root node if this is a mountpoint
+ if (FS.isMountpoint(current)) {
+ current = current.mount.root;
+ }
+
+ // follow symlinks
+ // by default, lookupPath will not follow a symlink if it is the final path component.
+ // setting opts.follow = true will override this behavior.
+ if (!islast || opts.follow) {
+ var count = 0;
+ while (FS.isLink(current.mode)) {
+ var link = FS.readlink(current_path);
+ current_path = PATH.resolve(PATH.dirname(current_path), link);
+
+ var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count });
+ current = lookup.node;
+
+ if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX).
+ throw new FS.ErrnoError(ERRNO_CODES.ELOOP);
+ }
+ }
+ }
+ }
+
+ return { path: current_path, node: current };
+ },
+ getPath: function(node) {
+ var path;
+ while (true) {
+ if (FS.isRoot(node)) {
+ return path ? PATH.join(node.mount.mountpoint, path) : node.mount.mountpoint;
+ }
+ path = path ? PATH.join(node.name, path) : node.name;
+ node = node.parent;
+ }
+ },
+
+ //
// nodes
//
hashName: function(parentid, name) {
@@ -56,19 +125,19 @@ mergeInto(LibraryManager.library, {
for (var i = 0; i < name.length; i++) {
hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0;
}
- return ((parentid + hash) >>> 0) % FS.name_table.length;
+ return ((parentid + hash) >>> 0) % FS.nameTable.length;
},
hashAddNode: function(node) {
var hash = FS.hashName(node.parent.id, node.name);
- node.name_next = FS.name_table[hash];
- FS.name_table[hash] = node;
+ node.name_next = FS.nameTable[hash];
+ FS.nameTable[hash] = node;
},
hashRemoveNode: function(node) {
var hash = FS.hashName(node.parent.id, node.name);
- if (FS.name_table[hash] === node) {
- FS.name_table[hash] = node.name_next;
+ if (FS.nameTable[hash] === node) {
+ FS.nameTable[hash] = node.name_next;
} else {
- var current = FS.name_table[hash];
+ var current = FS.nameTable[hash];
while (current) {
if (current.name_next === node) {
current.name_next = node.name_next;
@@ -84,7 +153,7 @@ mergeInto(LibraryManager.library, {
throw new FS.ErrnoError(err);
}
var hash = FS.hashName(parent.id, name);
- for (var node = FS.name_table[hash]; node; node = node.name_next) {
+ for (var node = FS.nameTable[hash]; node; node = node.name_next) {
if (node.parent.id === parent.id && node.name === name) {
return node;
}
@@ -159,75 +228,8 @@ mergeInto(LibraryManager.library, {
isFIFO: function(mode) {
return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFIFO') }}};
},
-
- //
- // paths
- //
- cwd: function() {
- return FS.currentPath;
- },
- lookupPath: function(path, opts) {
- path = PATH.resolve(FS.currentPath, path);
- opts = opts || { recurse_count: 0 };
-
- if (opts.recurse_count > 8) { // max recursive lookup of 8
- throw new FS.ErrnoError(ERRNO_CODES.ELOOP);
- }
-
- // split the path
- var parts = PATH.normalizeArray(path.split('/').filter(function(p) {
- return !!p;
- }), false);
-
- // start at the root
- var current = FS.root;
- var current_path = '/';
-
- for (var i = 0; i < parts.length; i++) {
- var islast = (i === parts.length-1);
- if (islast && opts.parent) {
- // stop resolving
- break;
- }
-
- current = FS.lookupNode(current, parts[i]);
- current_path = PATH.join(current_path, parts[i]);
-
- // jump to the mount's root node if this is a mountpoint
- if (FS.isMountpoint(current)) {
- current = current.mount.root;
- }
-
- // follow symlinks
- // by default, lookupPath will not follow a symlink if it is the final path component.
- // setting opts.follow = true will override this behavior.
- if (!islast || opts.follow) {
- var count = 0;
- while (FS.isLink(current.mode)) {
- var link = FS.readlink(current_path);
- current_path = PATH.resolve(PATH.dirname(current_path), link);
-
- var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count });
- current = lookup.node;
-
- if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX).
- throw new FS.ErrnoError(ERRNO_CODES.ELOOP);
- }
- }
- }
- }
-
- return { path: current_path, node: current };
- },
- getPath: function(node) {
- var path;
- while (true) {
- if (FS.isRoot(node)) {
- return path ? PATH.join(node.mount.mountpoint, path) : node.mount.mountpoint;
- }
- path = path ? PATH.join(node.name, path) : node.name;
- node = node.parent;
- }
+ isSocket: function(mode) {
+ return (mode & {{{ cDefine('S_IFSOCK') }}}) === {{{ cDefine('S_IFSOCK') }}};
},
//
@@ -284,18 +286,6 @@ mergeInto(LibraryManager.library, {
mayLookup: function(dir) {
return FS.nodePermissions(dir, 'x');
},
- mayMknod: function(mode) {
- switch (mode & {{{ cDefine('S_IFMT') }}}) {
- case {{{ cDefine('S_IFREG') }}}:
- case {{{ cDefine('S_IFCHR') }}}:
- case {{{ cDefine('S_IFBLK') }}}:
- case {{{ cDefine('S_IFIFO') }}}:
- case {{{ cDefine('S_IFSOCK') }}}:
- return 0;
- default:
- return ERRNO_CODES.EINVAL;
- }
- },
mayCreate: function(dir, name) {
try {
var node = FS.lookupNode(dir, name);
@@ -345,45 +335,6 @@ mergeInto(LibraryManager.library, {
},
//
- // devices
- //
- // each character device consists of a device id + stream operations.
- // when a character device node is created (e.g. /dev/stdin) it is
- // assigned a device id that lets us map back to the actual device.
- // by default, each character device stream (e.g. _stdin) uses chrdev_stream_ops.
- // however, once opened, the stream's operations are overridden with
- // the operations of the device its underlying node maps back to.
- chrdev_stream_ops: {
- open: function(stream) {
- var device = FS.getDevice(stream.node.rdev);
- // override node's stream ops with the device's
- stream.stream_ops = device.stream_ops;
- // forward the open call
- if (stream.stream_ops.open) {
- stream.stream_ops.open(stream);
- }
- },
- llseek: function() {
- throw new FS.ErrnoError(ERRNO_CODES.ESPIPE);
- }
- },
- major: function(dev) {
- return ((dev) >> 8);
- },
- minor: function(dev) {
- return ((dev) & 0xff);
- },
- makedev: function(ma, mi) {
- return ((ma) << 8 | (mi));
- },
- registerDevice: function(dev, ops) {
- FS.devices[dev] = { stream_ops: ops };
- },
- getDevice: function(dev) {
- return FS.devices[dev];
- },
-
- //
// streams
//
MAX_OPEN_FDS: 4096,
@@ -400,6 +351,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;
@@ -427,563 +381,46 @@ mergeInto(LibraryManager.library, {
},
//
- // compatibility
+ // devices
//
- getMode: function(canRead, canWrite) {
- var mode = 0;
- if (canRead) mode |= {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}};
- if (canWrite) mode |= {{{ cDefine('S_IWUGO') }}};
- return mode;
- },
- joinPath: function(parts, forceRelative) {
- var path = PATH.join.apply(null, parts);
- if (forceRelative && path[0] == '/') path = path.substr(1);
- return path;
- },
- absolutePath: function(relative, base) {
- return PATH.resolve(base, relative);
- },
- standardizePath: function(path) {
- return PATH.normalize(path);
- },
- findObject: function(path, dontResolveLastLink) {
- var ret = FS.analyzePath(path, dontResolveLastLink);
- if (ret.exists) {
- return ret.object;
- } else {
- ___setErrNo(ret.error);
- return null;
- }
- },
- analyzePath: function(path, dontResolveLastLink) {
- // operate from within the context of the symlink's target
- try {
- var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });
- path = lookup.path;
- } catch (e) {
- }
- var ret = {
- isRoot: false, exists: false, error: 0, name: null, path: null, object: null,
- parentExists: false, parentPath: null, parentObject: null
- };
- try {
- var lookup = FS.lookupPath(path, { parent: true });
- ret.parentExists = true;
- ret.parentPath = lookup.path;
- ret.parentObject = lookup.node;
- ret.name = PATH.basename(path);
- lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });
- ret.exists = true;
- ret.path = lookup.path;
- ret.object = lookup.node;
- ret.name = lookup.node.name;
- ret.isRoot = lookup.path === '/';
- } catch (e) {
- ret.error = e.errno;
- };
- return ret;
- },
- createFolder: function(parent, name, canRead, canWrite) {
- var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
- var mode = FS.getMode(canRead, canWrite);
- return FS.mkdir(path, mode);
- },
- createPath: function(parent, path, canRead, canWrite) {
- parent = typeof parent === 'string' ? parent : FS.getPath(parent);
- var parts = path.split('/').reverse();
- while (parts.length) {
- var part = parts.pop();
- if (!part) continue;
- var current = PATH.join(parent, part);
- try {
- FS.mkdir(current, 0777);
- } catch (e) {
- // ignore EEXIST
- }
- parent = current;
- }
- return current;
- },
- createFile: function(parent, name, properties, canRead, canWrite) {
- var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
- var mode = FS.getMode(canRead, canWrite);
- return FS.create(path, mode);
- },
- createDataFile: function(parent, name, data, canRead, canWrite, canOwn) {
- var path = name ? PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent;
- var mode = FS.getMode(canRead, canWrite);
- var node = FS.create(path, mode);
- if (data) {
- if (typeof data === 'string') {
- var arr = new Array(data.length);
- for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i);
- data = arr;
- }
- // make sure we can write to the file
- FS.chmod(path, mode | {{{ cDefine('S_IWUGO') }}});
- var stream = FS.open(path, 'w');
- FS.write(stream, data, 0, data.length, 0, canOwn);
- FS.close(stream);
- FS.chmod(path, mode);
- }
- return node;
- },
- createDevice: function(parent, name, input, output) {
- var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
- var mode = FS.getMode(!!input, !!output);
- if (!FS.createDevice.major) FS.createDevice.major = 64;
- var dev = FS.makedev(FS.createDevice.major++, 0);
- // Create a fake device that a set of stream ops to emulate
- // the old behavior.
- FS.registerDevice(dev, {
- open: function(stream) {
- stream.seekable = false;
- },
- close: function(stream) {
- // flush any pending line data
- if (output && output.buffer && output.buffer.length) {
- output({{{ charCode('\n') }}});
- }
- },
- read: function(stream, buffer, offset, length, pos /* ignored */) {
- var bytesRead = 0;
- for (var i = 0; i < length; i++) {
- var result;
- try {
- result = input();
- } catch (e) {
- throw new FS.ErrnoError(ERRNO_CODES.EIO);
- }
- if (result === undefined && bytesRead === 0) {
- throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
- }
- if (result === null || result === undefined) break;
- bytesRead++;
- buffer[offset+i] = result;
- }
- if (bytesRead) {
- stream.node.timestamp = Date.now();
- }
- return bytesRead;
- },
- write: function(stream, buffer, offset, length, pos) {
- for (var i = 0; i < length; i++) {
- try {
- output(buffer[offset+i]);
- } catch (e) {
- throw new FS.ErrnoError(ERRNO_CODES.EIO);
- }
- }
- if (length) {
- stream.node.timestamp = Date.now();
- }
- return i;
- }
- });
- return FS.mkdev(path, mode, dev);
- },
- createLink: function(parent, name, target, canRead, canWrite) {
- var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
- return FS.symlink(target, path);
- },
- // Makes sure a file's contents are loaded. Returns whether the file has
- // been loaded successfully. No-op for files that have been loaded already.
- forceLoadFile: function(obj) {
- if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true;
- var success = true;
- if (typeof XMLHttpRequest !== 'undefined') {
- throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");
- } else if (Module['read']) {
- // Command-line.
- try {
- // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as
- // read() will try to parse UTF8.
- obj.contents = intArrayFromString(Module['read'](obj.url), true);
- } catch (e) {
- success = false;
- }
- } else {
- throw new Error('Cannot load without read() or XMLHttpRequest.');
- }
- if (!success) ___setErrNo(ERRNO_CODES.EIO);
- return success;
- },
- // Creates a file record for lazy-loading from a URL. XXX This requires a synchronous
- // XHR, which is not possible in browsers except in a web worker! Use preloading,
- // either --preload-file in emcc or FS.createPreloadedFile
- createLazyFile: function(parent, name, url, canRead, canWrite) {
- if (typeof XMLHttpRequest !== 'undefined') {
- if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';
- // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.
- var LazyUint8Array = function() {
- this.lengthKnown = false;
- this.chunks = []; // Loaded chunks. Index is the chunk number
- }
- LazyUint8Array.prototype.get = function(idx) {
- if (idx > this.length-1 || idx < 0) {
- return undefined;
- }
- var chunkOffset = idx % this.chunkSize;
- var chunkNum = Math.floor(idx / this.chunkSize);
- return this.getter(chunkNum)[chunkOffset];
- }
- LazyUint8Array.prototype.setDataGetter = function(getter) {
- this.getter = getter;
- }
- LazyUint8Array.prototype.cacheLength = function() {
- // Find length
- var xhr = new XMLHttpRequest();
- xhr.open('HEAD', url, false);
- xhr.send(null);
- if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
- var datalength = Number(xhr.getResponseHeader("Content-length"));
- var header;
- var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
-#if SMALL_XHR_CHUNKS
- var chunkSize = 1024; // Chunk size in bytes
-#else
- var chunkSize = 1024*1024; // Chunk size in bytes
-#endif
-
- if (!hasByteServing) chunkSize = datalength;
-
- // Function to get a range from the remote URL.
- var doXHR = (function(from, to) {
- if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
- if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
-
- // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
- var xhr = new XMLHttpRequest();
- xhr.open('GET', url, false);
- if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
-
- // Some hints to the browser that we want binary data.
- if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
- if (xhr.overrideMimeType) {
- xhr.overrideMimeType('text/plain; charset=x-user-defined');
- }
-
- xhr.send(null);
- if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
- if (xhr.response !== undefined) {
- return new Uint8Array(xhr.response || []);
- } else {
- return intArrayFromString(xhr.responseText || '', true);
- }
- });
- var lazyArray = this;
- lazyArray.setDataGetter(function(chunkNum) {
- var start = chunkNum * chunkSize;
- var end = (chunkNum+1) * chunkSize - 1; // including this byte
- end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
- if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
- lazyArray.chunks[chunkNum] = doXHR(start, end);
- }
- if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
- return lazyArray.chunks[chunkNum];
- });
-
- this._length = datalength;
- this._chunkSize = chunkSize;
- this.lengthKnown = true;
- }
-
- var lazyArray = new LazyUint8Array();
- Object.defineProperty(lazyArray, "length", {
- get: function() {
- if(!this.lengthKnown) {
- this.cacheLength();
- }
- return this._length;
- }
- });
- Object.defineProperty(lazyArray, "chunkSize", {
- get: function() {
- if(!this.lengthKnown) {
- this.cacheLength();
- }
- return this._chunkSize;
- }
- });
-
- var properties = { isDevice: false, contents: lazyArray };
- } else {
- var properties = { isDevice: false, url: url };
- }
-
- var node = FS.createFile(parent, name, properties, canRead, canWrite);
- // This is a total hack, but I want to get this lazy file code out of the
- // core of MEMFS. If we want to keep this lazy file concept I feel it should
- // be its own thin LAZYFS proxying calls to MEMFS.
- if (properties.contents) {
- node.contents = properties.contents;
- } else if (properties.url) {
- node.contents = null;
- node.url = properties.url;
- }
- // override each stream op with one that tries to force load the lazy file first
- var stream_ops = {};
- var keys = Object.keys(node.stream_ops);
- keys.forEach(function(key) {
- var fn = node.stream_ops[key];
- stream_ops[key] = function() {
- if (!FS.forceLoadFile(node)) {
- throw new FS.ErrnoError(ERRNO_CODES.EIO);
- }
- return fn.apply(null, arguments);
- };
- });
- // use a custom read function
- stream_ops.read = function(stream, buffer, offset, length, position) {
- if (!FS.forceLoadFile(node)) {
- throw new FS.ErrnoError(ERRNO_CODES.EIO);
- }
- var contents = stream.node.contents;
- var size = Math.min(contents.length - position, length);
- if (contents.slice) { // normal array
- for (var i = 0; i < size; i++) {
- buffer[offset + i] = contents[position + i];
- }
- } else {
- for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR
- buffer[offset + i] = contents.get(position + i);
- }
- }
- return size;
- };
- node.stream_ops = stream_ops;
- return node;
- },
- // Preloads a file asynchronously. You can call this before run, for example in
- // preRun. run will be delayed until this file arrives and is set up.
- // If you call it after run(), you may want to pause the main loop until it
- // completes, if so, you can use the onload parameter to be notified when
- // that happens.
- // In addition to normally creating the file, we also asynchronously preload
- // the browser-friendly versions of it: For an image, we preload an Image
- // element and for an audio, and Audio. These are necessary for SDL_Image
- // and _Mixer to find the files in preloadedImages/Audios.
- // You can also call this with a typed array instead of a url. It will then
- // do preloading for the Image/Audio part, as if the typed array were the
- // result of an XHR that you did manually.
- createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn) {
- Browser.init();
- // TODO we should allow people to just pass in a complete filename instead
- // of parent and name being that we just join them anyways
- var fullname = name ? PATH.resolve(PATH.join(parent, name)) : parent;
- function processData(byteArray) {
- function finish(byteArray) {
- if (!dontCreateFile) {
- FS.createDataFile(parent, name, byteArray, canRead, canWrite, canOwn);
- }
- if (onload) onload();
- removeRunDependency('cp ' + fullname);
- }
- var handled = false;
- Module['preloadPlugins'].forEach(function(plugin) {
- if (handled) return;
- if (plugin['canHandle'](fullname)) {
- plugin['handle'](byteArray, fullname, finish, function() {
- if (onerror) onerror();
- removeRunDependency('cp ' + fullname);
- });
- handled = true;
- }
- });
- if (!handled) finish(byteArray);
- }
- addRunDependency('cp ' + fullname);
- if (typeof url == 'string') {
- Browser.asyncLoad(url, function(byteArray) {
- processData(byteArray);
- }, onerror);
- } else {
- processData(url);
- }
- },
-
- indexedDB: function() {
- return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
- },
-
- DB_NAME: function() {
- return 'EM_FS_' + window.location.pathname;
- },
- DB_VERSION: 20,
- DB_STORE_NAME: 'FILE_DATA',
-
- // asynchronously saves a list of files to an IndexedDB. The DB will be created if not already existing.
- saveFilesToDB: function(paths, onload, onerror) {
- onload = onload || function(){};
- onerror = onerror || function(){};
- var indexedDB = FS.indexedDB();
- try {
- var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
- } catch (e) {
- return onerror(e);
- }
- openRequest.onupgradeneeded = function() {
- console.log('creating db');
- var db = openRequest.result;
- db.createObjectStore(FS.DB_STORE_NAME);
- };
- openRequest.onsuccess = function() {
- var db = openRequest.result;
- var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite');
- var files = transaction.objectStore(FS.DB_STORE_NAME);
- var ok = 0, fail = 0, total = paths.length;
- function finish() {
- if (fail == 0) onload(); else onerror();
+ // each character device consists of a device id + stream operations.
+ // when a character device node is created (e.g. /dev/stdin) it is
+ // assigned a device id that lets us map back to the actual device.
+ // by default, each character device stream (e.g. _stdin) uses chrdev_stream_ops.
+ // however, once opened, the stream's operations are overridden with
+ // the operations of the device its underlying node maps back to.
+ chrdev_stream_ops: {
+ open: function(stream) {
+ var device = FS.getDevice(stream.node.rdev);
+ // override node's stream ops with the device's
+ stream.stream_ops = device.stream_ops;
+ // forward the open call
+ if (stream.stream_ops.open) {
+ stream.stream_ops.open(stream);
}
- paths.forEach(function(path) {
- var putRequest = files.put(FS.analyzePath(path).object.contents, path);
- putRequest.onsuccess = function() { ok++; if (ok + fail == total) finish() };
- putRequest.onerror = function() { fail++; if (ok + fail == total) finish() };
- });
- transaction.onerror = onerror;
- };
- openRequest.onerror = onerror;
- },
-
- // asychronously loads a file from IndexedDB.
- loadFilesFromDB: function(paths, onload, onerror) {
- onload = onload || function(){};
- onerror = onerror || function(){};
- var indexedDB = FS.indexedDB();
- try {
- var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
- } catch (e) {
- return onerror(e);
+ },
+ llseek: function() {
+ throw new FS.ErrnoError(ERRNO_CODES.ESPIPE);
}
- openRequest.onupgradeneeded = onerror; // no database to load from
- openRequest.onsuccess = function() {
- var db = openRequest.result;
- try {
- var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly');
- } catch(e) {
- onerror(e);
- return;
- }
- var files = transaction.objectStore(FS.DB_STORE_NAME);
- var ok = 0, fail = 0, total = paths.length;
- function finish() {
- if (fail == 0) onload(); else onerror();
- }
- paths.forEach(function(path) {
- var getRequest = files.get(path);
- getRequest.onsuccess = function() {
- if (FS.analyzePath(path).exists) {
- FS.unlink(path);
- }
- FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true);
- ok++;
- if (ok + fail == total) finish();
- };
- getRequest.onerror = function() { fail++; if (ok + fail == total) finish() };
- });
- transaction.onerror = onerror;
- };
- openRequest.onerror = onerror;
- },
-
- //
- // general
- //
- createDefaultDirectories: function() {
- FS.mkdir('/tmp', 0777);
},
- createDefaultDevices: function() {
- // create /dev
- FS.mkdir('/dev', 0777);
- // setup /dev/null
- FS.registerDevice(FS.makedev(1, 3), {
- read: function() { return 0; },
- write: function() { return 0; }
- });
- FS.mkdev('/dev/null', 0666, FS.makedev(1, 3));
- // setup /dev/tty and /dev/tty1
- // stderr needs to print output using Module['printErr']
- // so we register a second tty just for it.
- TTY.register(FS.makedev(5, 0), TTY.default_tty_ops);
- TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops);
- FS.mkdev('/dev/tty', 0666, FS.makedev(5, 0));
- FS.mkdev('/dev/tty1', 0666, FS.makedev(6, 0));
- // we're not going to emulate the actual shm device,
- // just create the tmp dirs that reside in it commonly
- FS.mkdir('/dev/shm', 0777);
- FS.mkdir('/dev/shm/tmp', 0777);
+ major: function(dev) {
+ return ((dev) >> 8);
},
- createStandardStreams: function() {
- // TODO deprecate the old functionality of a single
- // input / output callback and that utilizes FS.createDevice
- // and instead require a unique set of stream ops
-
- // by default, we symlink the standard streams to the
- // default tty devices. however, if the standard streams
- // have been overwritten we create a unique device for
- // them instead.
- if (Module['stdin']) {
- FS.createDevice('/dev', 'stdin', Module['stdin']);
- } else {
- FS.symlink('/dev/tty', '/dev/stdin');
- }
- if (Module['stdout']) {
- FS.createDevice('/dev', 'stdout', null, Module['stdout']);
- } else {
- FS.symlink('/dev/tty', '/dev/stdout');
- }
- if (Module['stderr']) {
- FS.createDevice('/dev', 'stderr', null, Module['stderr']);
- } else {
- FS.symlink('/dev/tty1', '/dev/stderr');
- }
-
- // open default streams for the stdin, stdout and stderr devices
- var stdin = FS.open('/dev/stdin', 'r');
- {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 'stdin.fd', 'void*') }}};
- assert(stdin.fd === 1, 'invalid handle for stdin (' + stdin.fd + ')');
-
- var stdout = FS.open('/dev/stdout', 'w');
- {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 'stdout.fd', 'void*') }}};
- assert(stdout.fd === 2, 'invalid handle for stdout (' + stdout.fd + ')');
-
- var stderr = FS.open('/dev/stderr', 'w');
- {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 'stderr.fd', 'void*') }}};
- assert(stderr.fd === 3, 'invalid handle for stderr (' + stderr.fd + ')');
+ minor: function(dev) {
+ return ((dev) & 0xff);
},
- staticInit: function() {
- FS.name_table = new Array(4096);
-
- FS.root = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0);
- FS.mount(MEMFS, {}, '/');
-
- FS.createDefaultDirectories();
- FS.createDefaultDevices();
+ makedev: function(ma, mi) {
+ return ((ma) << 8 | (mi));
},
- init: function(input, output, error) {
- assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)');
- FS.init.initialized = true;
-
- // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here
- Module['stdin'] = input || Module['stdin'];
- Module['stdout'] = output || Module['stdout'];
- Module['stderr'] = error || Module['stderr'];
-
- FS.createStandardStreams();
+ registerDevice: function(dev, ops) {
+ FS.devices[dev] = { stream_ops: ops };
},
- quit: function() {
- FS.init.initialized = false;
- for (var i = 0; i < FS.streams.length; i++) {
- var stream = FS.streams[i];
- if (!stream) {
- continue;
- }
- FS.close(stream);
- }
+ getDevice: function(dev) {
+ return FS.devices[dev];
},
//
- // vfs functionality
+ // core
//
mount: function(type, opts, mountpoint) {
var mount = {
@@ -1030,16 +467,22 @@ mergeInto(LibraryManager.library, {
},
// helpers to create specific types of nodes
create: function(path, mode) {
+ mode = mode !== undefined ? mode : 0666;
mode &= {{{ cDefine('S_IALLUGO') }}};
mode |= {{{ cDefine('S_IFREG') }}};
return FS.mknod(path, mode, 0);
},
mkdir: function(path, mode) {
+ mode = mode !== undefined ? mode : 0777;
mode &= {{{ cDefine('S_IRWXUGO') }}} | {{{ cDefine('S_ISVTX') }}};
mode |= {{{ cDefine('S_IFDIR') }}};
return FS.mknod(path, mode, 0);
},
mkdev: function(path, mode, dev) {
+ if (typeof(dev) === 'undefined') {
+ dev = mode;
+ mode = 0666;
+ }
mode |= {{{ cDefine('S_IFCHR') }}};
return FS.mknod(path, mode, dev);
},
@@ -1459,6 +902,612 @@ 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);
+ },
+ readFile: function(path, opts) {
+ opts = opts || {};
+ opts.flags = opts.flags || 'r';
+ opts.encoding = opts.encoding || 'binary';
+ var ret;
+ var stream = FS.open(path, opts.flags);
+ var stat = FS.stat(path);
+ var length = stat.size;
+ var buf = new Uint8Array(length);
+ FS.read(stream, buf, 0, length, 0);
+ if (opts.encoding === 'utf8') {
+ ret = '';
+ var utf8 = new Runtime.UTF8Processor();
+ for (var i = 0; i < length; i++) {
+ ret += utf8.processCChar(buf[i]);
+ }
+ } else if (opts.encoding === 'binary') {
+ ret = buf;
+ } else {
+ throw new Error('Invalid encoding type "' + opts.encoding + '"');
+ }
+ FS.close(stream);
+ return ret;
+ },
+ writeFile: function(path, data, opts) {
+ opts = opts || {};
+ opts.flags = opts.flags || 'w';
+ opts.encoding = opts.encoding || 'utf8';
+ var stream = FS.open(path, opts.flags, opts.mode);
+ if (opts.encoding === 'utf8') {
+ var utf8 = new Runtime.UTF8Processor();
+ var buf = new Uint8Array(utf8.processJSString(data));
+ FS.write(stream, buf, 0, buf.length, 0);
+ } else if (opts.encoding === 'binary') {
+ FS.write(stream, data, 0, data.length, 0);
+ } else {
+ throw new Error('Invalid encoding type "' + opts.encoding + '"');
+ }
+ FS.close(stream);
+ },
+
+ //
+ // module-level FS code
+ // TODO move to pre/postamble
+ //
+ createDefaultDirectories: function() {
+ FS.mkdir('/tmp');
+ },
+ createDefaultDevices: function() {
+ // create /dev
+ FS.mkdir('/dev');
+ // setup /dev/null
+ FS.registerDevice(FS.makedev(1, 3), {
+ read: function() { return 0; },
+ write: function() { return 0; }
+ });
+ FS.mkdev('/dev/null', FS.makedev(1, 3));
+ // setup /dev/tty and /dev/tty1
+ // stderr needs to print output using Module['printErr']
+ // so we register a second tty just for it.
+ TTY.register(FS.makedev(5, 0), TTY.default_tty_ops);
+ TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops);
+ FS.mkdev('/dev/tty', FS.makedev(5, 0));
+ FS.mkdev('/dev/tty1', FS.makedev(6, 0));
+ // we're not going to emulate the actual shm device,
+ // just create the tmp dirs that reside in it commonly
+ FS.mkdir('/dev/shm');
+ FS.mkdir('/dev/shm/tmp');
+ },
+ createStandardStreams: function() {
+ // TODO deprecate the old functionality of a single
+ // input / output callback and that utilizes FS.createDevice
+ // and instead require a unique set of stream ops
+
+ // by default, we symlink the standard streams to the
+ // default tty devices. however, if the standard streams
+ // have been overwritten we create a unique device for
+ // them instead.
+ if (Module['stdin']) {
+ FS.createDevice('/dev', 'stdin', Module['stdin']);
+ } else {
+ FS.symlink('/dev/tty', '/dev/stdin');
+ }
+ if (Module['stdout']) {
+ FS.createDevice('/dev', 'stdout', null, Module['stdout']);
+ } else {
+ FS.symlink('/dev/tty', '/dev/stdout');
+ }
+ if (Module['stderr']) {
+ FS.createDevice('/dev', 'stderr', null, Module['stderr']);
+ } else {
+ FS.symlink('/dev/tty1', '/dev/stderr');
+ }
+
+ // open default streams for the stdin, stdout and stderr devices
+ var stdin = FS.open('/dev/stdin', 'r');
+ {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 'stdin.fd', 'void*') }}};
+ assert(stdin.fd === 1, 'invalid handle for stdin (' + stdin.fd + ')');
+
+ var stdout = FS.open('/dev/stdout', 'w');
+ {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 'stdout.fd', 'void*') }}};
+ assert(stdout.fd === 2, 'invalid handle for stdout (' + stdout.fd + ')');
+
+ var stderr = FS.open('/dev/stderr', 'w');
+ {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 'stderr.fd', 'void*') }}};
+ assert(stderr.fd === 3, 'invalid handle for stderr (' + stderr.fd + ')');
+ },
+ staticInit: function() {
+ FS.nameTable = new Array(4096);
+
+ FS.root = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0);
+ FS.mount(MEMFS, {}, '/');
+
+ FS.createDefaultDirectories();
+ FS.createDefaultDevices();
+ },
+ init: function(input, output, error) {
+ assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)');
+ FS.init.initialized = true;
+
+ // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here
+ Module['stdin'] = input || Module['stdin'];
+ Module['stdout'] = output || Module['stdout'];
+ Module['stderr'] = error || Module['stderr'];
+
+ FS.createStandardStreams();
+ },
+ quit: function() {
+ FS.init.initialized = false;
+ for (var i = 0; i < FS.streams.length; i++) {
+ var stream = FS.streams[i];
+ if (!stream) {
+ continue;
+ }
+ FS.close(stream);
+ }
+ },
+
+ //
+ // old v1 compatibility functions
+ //
+ getMode: function(canRead, canWrite) {
+ var mode = 0;
+ if (canRead) mode |= {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}};
+ if (canWrite) mode |= {{{ cDefine('S_IWUGO') }}};
+ return mode;
+ },
+ joinPath: function(parts, forceRelative) {
+ var path = PATH.join.apply(null, parts);
+ if (forceRelative && path[0] == '/') path = path.substr(1);
+ return path;
+ },
+ absolutePath: function(relative, base) {
+ return PATH.resolve(base, relative);
+ },
+ standardizePath: function(path) {
+ return PATH.normalize(path);
+ },
+ findObject: function(path, dontResolveLastLink) {
+ var ret = FS.analyzePath(path, dontResolveLastLink);
+ if (ret.exists) {
+ return ret.object;
+ } else {
+ ___setErrNo(ret.error);
+ return null;
+ }
+ },
+ analyzePath: function(path, dontResolveLastLink) {
+ // operate from within the context of the symlink's target
+ try {
+ var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });
+ path = lookup.path;
+ } catch (e) {
+ }
+ var ret = {
+ isRoot: false, exists: false, error: 0, name: null, path: null, object: null,
+ parentExists: false, parentPath: null, parentObject: null
+ };
+ try {
+ var lookup = FS.lookupPath(path, { parent: true });
+ ret.parentExists = true;
+ ret.parentPath = lookup.path;
+ ret.parentObject = lookup.node;
+ ret.name = PATH.basename(path);
+ lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });
+ ret.exists = true;
+ ret.path = lookup.path;
+ ret.object = lookup.node;
+ ret.name = lookup.node.name;
+ ret.isRoot = lookup.path === '/';
+ } catch (e) {
+ ret.error = e.errno;
+ };
+ return ret;
+ },
+ createFolder: function(parent, name, canRead, canWrite) {
+ var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var mode = FS.getMode(canRead, canWrite);
+ return FS.mkdir(path, mode);
+ },
+ createPath: function(parent, path, canRead, canWrite) {
+ parent = typeof parent === 'string' ? parent : FS.getPath(parent);
+ var parts = path.split('/').reverse();
+ while (parts.length) {
+ var part = parts.pop();
+ if (!part) continue;
+ var current = PATH.join(parent, part);
+ try {
+ FS.mkdir(current);
+ } catch (e) {
+ // ignore EEXIST
+ }
+ parent = current;
+ }
+ return current;
+ },
+ createFile: function(parent, name, properties, canRead, canWrite) {
+ var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var mode = FS.getMode(canRead, canWrite);
+ return FS.create(path, mode);
+ },
+ createDataFile: function(parent, name, data, canRead, canWrite, canOwn) {
+ var path = name ? PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent;
+ var mode = FS.getMode(canRead, canWrite);
+ var node = FS.create(path, mode);
+ if (data) {
+ if (typeof data === 'string') {
+ var arr = new Array(data.length);
+ for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i);
+ data = arr;
+ }
+ // make sure we can write to the file
+ FS.chmod(path, mode | {{{ cDefine('S_IWUGO') }}});
+ var stream = FS.open(path, 'w');
+ FS.write(stream, data, 0, data.length, 0, canOwn);
+ FS.close(stream);
+ FS.chmod(path, mode);
+ }
+ return node;
+ },
+ createDevice: function(parent, name, input, output) {
+ var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var mode = FS.getMode(!!input, !!output);
+ if (!FS.createDevice.major) FS.createDevice.major = 64;
+ var dev = FS.makedev(FS.createDevice.major++, 0);
+ // Create a fake device that a set of stream ops to emulate
+ // the old behavior.
+ FS.registerDevice(dev, {
+ open: function(stream) {
+ stream.seekable = false;
+ },
+ close: function(stream) {
+ // flush any pending line data
+ if (output && output.buffer && output.buffer.length) {
+ output({{{ charCode('\n') }}});
+ }
+ },
+ read: function(stream, buffer, offset, length, pos /* ignored */) {
+ var bytesRead = 0;
+ for (var i = 0; i < length; i++) {
+ var result;
+ try {
+ result = input();
+ } catch (e) {
+ throw new FS.ErrnoError(ERRNO_CODES.EIO);
+ }
+ if (result === undefined && bytesRead === 0) {
+ throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
+ }
+ if (result === null || result === undefined) break;
+ bytesRead++;
+ buffer[offset+i] = result;
+ }
+ if (bytesRead) {
+ stream.node.timestamp = Date.now();
+ }
+ return bytesRead;
+ },
+ write: function(stream, buffer, offset, length, pos) {
+ for (var i = 0; i < length; i++) {
+ try {
+ output(buffer[offset+i]);
+ } catch (e) {
+ throw new FS.ErrnoError(ERRNO_CODES.EIO);
+ }
+ }
+ if (length) {
+ stream.node.timestamp = Date.now();
+ }
+ return i;
+ }
+ });
+ return FS.mkdev(path, mode, dev);
+ },
+ createLink: function(parent, name, target, canRead, canWrite) {
+ var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ return FS.symlink(target, path);
+ },
+ // Makes sure a file's contents are loaded. Returns whether the file has
+ // been loaded successfully. No-op for files that have been loaded already.
+ forceLoadFile: function(obj) {
+ if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true;
+ var success = true;
+ if (typeof XMLHttpRequest !== 'undefined') {
+ throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");
+ } else if (Module['read']) {
+ // Command-line.
+ try {
+ // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as
+ // read() will try to parse UTF8.
+ obj.contents = intArrayFromString(Module['read'](obj.url), true);
+ } catch (e) {
+ success = false;
+ }
+ } else {
+ throw new Error('Cannot load without read() or XMLHttpRequest.');
+ }
+ if (!success) ___setErrNo(ERRNO_CODES.EIO);
+ return success;
+ },
+ // Creates a file record for lazy-loading from a URL. XXX This requires a synchronous
+ // XHR, which is not possible in browsers except in a web worker! Use preloading,
+ // either --preload-file in emcc or FS.createPreloadedFile
+ createLazyFile: function(parent, name, url, canRead, canWrite) {
+ if (typeof XMLHttpRequest !== 'undefined') {
+ if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';
+ // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.
+ var LazyUint8Array = function() {
+ this.lengthKnown = false;
+ this.chunks = []; // Loaded chunks. Index is the chunk number
+ }
+ LazyUint8Array.prototype.get = function(idx) {
+ if (idx > this.length-1 || idx < 0) {
+ return undefined;
+ }
+ var chunkOffset = idx % this.chunkSize;
+ var chunkNum = Math.floor(idx / this.chunkSize);
+ return this.getter(chunkNum)[chunkOffset];
+ }
+ LazyUint8Array.prototype.setDataGetter = function(getter) {
+ this.getter = getter;
+ }
+ LazyUint8Array.prototype.cacheLength = function() {
+ // Find length
+ var xhr = new XMLHttpRequest();
+ xhr.open('HEAD', url, false);
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ var datalength = Number(xhr.getResponseHeader("Content-length"));
+ var header;
+ var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
+#if SMALL_XHR_CHUNKS
+ var chunkSize = 1024; // Chunk size in bytes
+#else
+ var chunkSize = 1024*1024; // Chunk size in bytes
+#endif
+
+ if (!hasByteServing) chunkSize = datalength;
+
+ // Function to get a range from the remote URL.
+ var doXHR = (function(from, to) {
+ if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
+ if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
+
+ // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, false);
+ if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
+
+ // Some hints to the browser that we want binary data.
+ if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType('text/plain; charset=x-user-defined');
+ }
+
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ if (xhr.response !== undefined) {
+ return new Uint8Array(xhr.response || []);
+ } else {
+ return intArrayFromString(xhr.responseText || '', true);
+ }
+ });
+ var lazyArray = this;
+ lazyArray.setDataGetter(function(chunkNum) {
+ var start = chunkNum * chunkSize;
+ var end = (chunkNum+1) * chunkSize - 1; // including this byte
+ end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
+ if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
+ lazyArray.chunks[chunkNum] = doXHR(start, end);
+ }
+ if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
+ return lazyArray.chunks[chunkNum];
+ });
+
+ this._length = datalength;
+ this._chunkSize = chunkSize;
+ this.lengthKnown = true;
+ }
+
+ var lazyArray = new LazyUint8Array();
+ Object.defineProperty(lazyArray, "length", {
+ get: function() {
+ if(!this.lengthKnown) {
+ this.cacheLength();
+ }
+ return this._length;
+ }
+ });
+ Object.defineProperty(lazyArray, "chunkSize", {
+ get: function() {
+ if(!this.lengthKnown) {
+ this.cacheLength();
+ }
+ return this._chunkSize;
+ }
+ });
+
+ var properties = { isDevice: false, contents: lazyArray };
+ } else {
+ var properties = { isDevice: false, url: url };
+ }
+
+ var node = FS.createFile(parent, name, properties, canRead, canWrite);
+ // This is a total hack, but I want to get this lazy file code out of the
+ // core of MEMFS. If we want to keep this lazy file concept I feel it should
+ // be its own thin LAZYFS proxying calls to MEMFS.
+ if (properties.contents) {
+ node.contents = properties.contents;
+ } else if (properties.url) {
+ node.contents = null;
+ node.url = properties.url;
+ }
+ // override each stream op with one that tries to force load the lazy file first
+ var stream_ops = {};
+ var keys = Object.keys(node.stream_ops);
+ keys.forEach(function(key) {
+ var fn = node.stream_ops[key];
+ stream_ops[key] = function() {
+ if (!FS.forceLoadFile(node)) {
+ throw new FS.ErrnoError(ERRNO_CODES.EIO);
+ }
+ return fn.apply(null, arguments);
+ };
+ });
+ // use a custom read function
+ stream_ops.read = function(stream, buffer, offset, length, position) {
+ if (!FS.forceLoadFile(node)) {
+ throw new FS.ErrnoError(ERRNO_CODES.EIO);
+ }
+ var contents = stream.node.contents;
+ var size = Math.min(contents.length - position, length);
+ if (contents.slice) { // normal array
+ for (var i = 0; i < size; i++) {
+ buffer[offset + i] = contents[position + i];
+ }
+ } else {
+ for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR
+ buffer[offset + i] = contents.get(position + i);
+ }
+ }
+ return size;
+ };
+ node.stream_ops = stream_ops;
+ return node;
+ },
+ // Preloads a file asynchronously. You can call this before run, for example in
+ // preRun. run will be delayed until this file arrives and is set up.
+ // If you call it after run(), you may want to pause the main loop until it
+ // completes, if so, you can use the onload parameter to be notified when
+ // that happens.
+ // In addition to normally creating the file, we also asynchronously preload
+ // the browser-friendly versions of it: For an image, we preload an Image
+ // element and for an audio, and Audio. These are necessary for SDL_Image
+ // and _Mixer to find the files in preloadedImages/Audios.
+ // You can also call this with a typed array instead of a url. It will then
+ // do preloading for the Image/Audio part, as if the typed array were the
+ // result of an XHR that you did manually.
+ createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn) {
+ Browser.init();
+ // TODO we should allow people to just pass in a complete filename instead
+ // of parent and name being that we just join them anyways
+ var fullname = name ? PATH.resolve(PATH.join(parent, name)) : parent;
+ function processData(byteArray) {
+ function finish(byteArray) {
+ if (!dontCreateFile) {
+ FS.createDataFile(parent, name, byteArray, canRead, canWrite, canOwn);
+ }
+ if (onload) onload();
+ removeRunDependency('cp ' + fullname);
+ }
+ var handled = false;
+ Module['preloadPlugins'].forEach(function(plugin) {
+ if (handled) return;
+ if (plugin['canHandle'](fullname)) {
+ plugin['handle'](byteArray, fullname, finish, function() {
+ if (onerror) onerror();
+ removeRunDependency('cp ' + fullname);
+ });
+ handled = true;
+ }
+ });
+ if (!handled) finish(byteArray);
+ }
+ addRunDependency('cp ' + fullname);
+ if (typeof url == 'string') {
+ Browser.asyncLoad(url, function(byteArray) {
+ processData(byteArray);
+ }, onerror);
+ } else {
+ processData(url);
+ }
+ },
+
+ //
+ // persistence
+ //
+ indexedDB: function() {
+ return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
+ },
+
+ DB_NAME: function() {
+ return 'EM_FS_' + window.location.pathname;
+ },
+ DB_VERSION: 20,
+ DB_STORE_NAME: 'FILE_DATA',
+
+ // asynchronously saves a list of files to an IndexedDB. The DB will be created if not already existing.
+ saveFilesToDB: function(paths, onload, onerror) {
+ onload = onload || function(){};
+ onerror = onerror || function(){};
+ var indexedDB = FS.indexedDB();
+ try {
+ var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
+ } catch (e) {
+ return onerror(e);
+ }
+ openRequest.onupgradeneeded = function() {
+ console.log('creating db');
+ var db = openRequest.result;
+ db.createObjectStore(FS.DB_STORE_NAME);
+ };
+ openRequest.onsuccess = function() {
+ var db = openRequest.result;
+ var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite');
+ var files = transaction.objectStore(FS.DB_STORE_NAME);
+ var ok = 0, fail = 0, total = paths.length;
+ function finish() {
+ if (fail == 0) onload(); else onerror();
+ }
+ paths.forEach(function(path) {
+ var putRequest = files.put(FS.analyzePath(path).object.contents, path);
+ putRequest.onsuccess = function() { ok++; if (ok + fail == total) finish() };
+ putRequest.onerror = function() { fail++; if (ok + fail == total) finish() };
+ });
+ transaction.onerror = onerror;
+ };
+ openRequest.onerror = onerror;
+ },
+
+ // asychronously loads a file from IndexedDB.
+ loadFilesFromDB: function(paths, onload, onerror) {
+ onload = onload || function(){};
+ onerror = onerror || function(){};
+ var indexedDB = FS.indexedDB();
+ try {
+ var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
+ } catch (e) {
+ return onerror(e);
+ }
+ openRequest.onupgradeneeded = onerror; // no database to load from
+ openRequest.onsuccess = function() {
+ var db = openRequest.result;
+ try {
+ var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly');
+ } catch(e) {
+ onerror(e);
+ return;
+ }
+ var files = transaction.objectStore(FS.DB_STORE_NAME);
+ var ok = 0, fail = 0, total = paths.length;
+ function finish() {
+ if (fail == 0) onload(); else onerror();
+ }
+ paths.forEach(function(path) {
+ var getRequest = files.get(path);
+ getRequest.onsuccess = function() {
+ if (FS.analyzePath(path).exists) {
+ FS.unlink(path);
+ }
+ FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true);
+ ok++;
+ if (ok + fail == total) finish();
+ };
+ getRequest.onerror = function() { fail++; if (ok + fail == total) finish() };
+ });
+ transaction.onerror = onerror;
+ };
+ openRequest.onerror = onerror;
}
}
});
diff --git a/src/library_gl.js b/src/library_gl.js
index c134ad97..16ea5531 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -657,6 +657,20 @@ var LibraryGL = {
glBufferData__sig: 'viiii',
glBufferData: function(target, size, data, usage) {
+ switch (usage) { // fix usages, WebGL only has *_DRAW
+ case 0x88E1: // GL_STREAM_READ
+ case 0x88E2: // GL_STREAM_COPY
+ usage = 0x88E0; // GL_STREAM_DRAW
+ break;
+ case 0x88E5: // GL_STATIC_READ
+ case 0x88E6: // GL_STATIC_COPY
+ usage = 0x88E4; // GL_STATIC_DRAW
+ break;
+ case 0x88E9: // GL_DYNAMIC_READ
+ case 0x88EA: // GL_DYNAMIC_COPY
+ usage = 0x88E8; // GL_DYNAMIC_DRAW
+ break;
+ }
Module.ctx.bufferData(target, HEAPU8.subarray(data, data+size), usage);
},
@@ -3398,6 +3412,7 @@ var LibraryGL = {
// does not work for glBegin/End, where we generate renderer components dynamically and then
// disable them ourselves, but it does help with glDrawElements/Arrays.
if (!this.modifiedClientAttributes) {
+ GL.immediate.vertexCounter = (GL.immediate.stride * count) / 4; // XXX assuming float
return;
}
this.modifiedClientAttributes = false;
diff --git a/src/library_openal.js b/src/library_openal.js
index dea986f3..e8a2e223 100644
--- a/src/library_openal.js
+++ b/src/library_openal.js
@@ -8,12 +8,6 @@ var LibraryOpenAL = {
QUEUE_INTERVAL: 25,
QUEUE_LOOKAHEAD: 100,
- getCurrentTime: function(context) {
- // currentTime is frozen during execution, use performance timers to
- // emulate the current time at call time
- return (window['performance']['now']() - context.startTime) / 1000.0;
- },
-
updateSources: function(context) {
for (var i = 0; i < context.src.length; i++) {
AL.updateSource(context.src[i]);
@@ -28,7 +22,7 @@ var LibraryOpenAL = {
return;
}
- var currentTime = AL.getCurrentTime(AL.currentContext);
+ var currentTime = AL.currentContext.ctx.currentTime;
var startTime = src.bufferPosition;
for (var i = src.buffersPlayed; i < src.queue.length; i++) {
@@ -79,7 +73,7 @@ var LibraryOpenAL = {
if (src.state !== 0x1013 /* AL_PAUSED */) {
src.state = 0x1012 /* AL_PLAYING */;
// Reset our position.
- src.bufferPosition = AL.getCurrentTime(AL.currentContext);
+ src.bufferPosition = AL.currentContext.ctx.currentTime;
src.buffersPlayed = 0;
#if OPENAL_DEBUG
console.log('setSourceState resetting and playing source ' + idx);
@@ -87,7 +81,7 @@ var LibraryOpenAL = {
} else {
src.state = 0x1012 /* AL_PLAYING */;
// Use the current offset from src.bufferPosition to resume at the correct point.
- src.bufferPosition = AL.getCurrentTime(AL.currentContext) - src.bufferPosition;
+ src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition;
#if OPENAL_DEBUG
console.log('setSourceState resuming source ' + idx + ' at ' + src.bufferPosition.toFixed(4));
#endif
@@ -98,7 +92,7 @@ var LibraryOpenAL = {
if (src.state === 0x1012 /* AL_PLAYING */) {
src.state = 0x1013 /* AL_PAUSED */;
// Store off the current offset to restore with on resume.
- src.bufferPosition = AL.getCurrentTime(AL.currentContext) - src.bufferPosition;
+ src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition;
AL.stopSourceQueue(src);
#if OPENAL_DEBUG
console.log('setSourceState pausing source ' + idx + ' at ' + src.bufferPosition.toFixed(4));
@@ -212,8 +206,7 @@ var LibraryOpenAL = {
err: 0,
src: [],
buf: [],
- interval: setInterval(function() { AL.updateSources(context); }, AL.QUEUE_INTERVAL),
- startTime: window['performance']['now']()
+ interval: setInterval(function() { AL.updateSources(context); }, AL.QUEUE_INTERVAL)
};
AL.contexts.push(context);
return AL.contexts.length;
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 53239989..bf4a2472 100644
--- a/src/library_tty.js
+++ b/src/library_tty.js
@@ -6,23 +6,25 @@ mergeInto(LibraryManager.library, {
$TTY: {
ttys: [],
init: function () {
- 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');
- }
+ // 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() {
- 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']();
- }
+ // 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 };
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/parseTools.js b/src/parseTools.js
index 66354dca..90c5acab 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -35,8 +35,13 @@ function preprocess(text) {
var op = parts[2];
var value = parts[3];
if (op) {
- assert(op === '==')
- showStack.push(ident in this && this[ident] == value);
+ if (op === '==') {
+ showStack.push(ident in this && this[ident] == value);
+ } else if (op === '!=') {
+ showStack.push(!(ident in this && this[ident] == value));
+ } else {
+ error('unsupported preprecessor op ' + op);
+ }
} else {
showStack.push(ident in this && this[ident] > 0);
}
diff --git a/src/preamble.js b/src/preamble.js
index 4cae05a6..227b3043 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -595,9 +595,9 @@ var DYNAMIC_BASE = 0, DYNAMICTOP = 0; // dynamic area handled by sbrk
function enlargeMemory() {
#if ALLOW_MEMORY_GROWTH == 0
#if ASM_JS == 0
- abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value, (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.');
+ abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ' + TOTAL_MEMORY + ', (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.');
#else
- abort('Cannot enlarge memory arrays in asm.js. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value, or (2) set Module.TOTAL_MEMORY before the program runs.');
+ abort('Cannot enlarge memory arrays in asm.js. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ' + TOTAL_MEMORY + ', or (2) set Module.TOTAL_MEMORY before the program runs.');
#endif
#else
// TOTAL_MEMORY is the current size of the actual array, and DYNAMICTOP is the new top.
diff --git a/src/settings.js b/src/settings.js
index 02423ba5..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',
@@ -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/libcxx/__locale b/system/include/libcxx/__locale
index 93147ec0..5ae8fa59 100644
--- a/system/include/libcxx/__locale
+++ b/system/include/libcxx/__locale
@@ -21,9 +21,9 @@
#include <locale.h>
#ifdef _LIBCPP_MSVCRT
# include <support/win32/locale_win32.h>
-#elif (defined(__GLIBC__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun__)) || defined(EMSCRIPTEN)
+#elif (defined(__GLIBC__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun__)) || defined(__EMSCRIPTEN__)
# include <xlocale.h>
-#endif // _WIN32 || __GLIBC__ || __APPLE__ || __FreeBSD__ || __sun__ || EMSCRIPTEN
+#endif // _WIN32 || __GLIBC__ || __APPLE__ || __FreeBSD__ || __sun__ || __EMSCRIPTEN__
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
@@ -339,12 +339,12 @@ public:
static const mask punct = _PUNCT;
static const mask xdigit = _HEX;
static const mask blank = _BLANK;
-#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
#ifdef __APPLE__
typedef __uint32_t mask;
#elif defined(__FreeBSD__)
typedef unsigned long mask;
-#elif defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__EMSCRIPTEN__) || defined(__NetBSD__)
typedef unsigned short mask;
#endif
static const mask space = _CTYPE_S;
@@ -373,7 +373,7 @@ public:
static const mask punct = _ISPUNCT;
static const mask xdigit = _ISXDIGIT;
static const mask blank = _ISBLANK;
-#else // __GLIBC__ || _WIN32 || __APPLE__ || __FreeBSD__ || EMSCRIPTEN || __sun__
+#else // __GLIBC__ || _WIN32 || __APPLE__ || __FreeBSD__ || __EMSCRIPTEN__ || __sun__
typedef unsigned long mask;
static const mask space = 1<<0;
static const mask print = 1<<1;
@@ -596,7 +596,7 @@ public:
#endif
_LIBCPP_ALWAYS_INLINE const mask* table() const _NOEXCEPT {return __tab_;}
static const mask* classic_table() _NOEXCEPT;
-#if defined(__GLIBC__) || defined(EMSCRIPTEN)
+#if defined(__GLIBC__) || defined(__EMSCRIPTEN__)
static const int* __classic_upper_table() _NOEXCEPT;
static const int* __classic_lower_table() _NOEXCEPT;
#endif
diff --git a/system/include/libcxx/locale b/system/include/libcxx/locale
index 00a275f9..f5f5fff9 100644
--- a/system/include/libcxx/locale
+++ b/system/include/libcxx/locale
@@ -224,7 +224,7 @@ typedef _VSTD::unique_ptr<__locale_struct, decltype(&uselocale)> __locale_raii;
// OSX has nice foo_l() functions that let you turn off use of the global
// locale. Linux, not so much. The following functions avoid the locale when
// that's possible and otherwise do the wrong thing. FIXME.
-#if defined(__linux__) || defined(EMSCRIPTEN)
+#if defined(__linux__) || defined(__EMSCRIPTEN__)
#ifdef _LIBCPP_LOCALE__L_EXTENSIONS
decltype(MB_CUR_MAX_L(_VSTD::declval<locale_t>()))
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/system/lib/libcxx/exception.cpp b/system/lib/libcxx/exception.cpp
index d3e1b292..3487bd8b 100644
--- a/system/lib/libcxx/exception.cpp
+++ b/system/lib/libcxx/exception.cpp
@@ -78,7 +78,7 @@ get_terminate() _NOEXCEPT
return __sync_fetch_and_add(&__terminate_handler, (terminate_handler)0);
}
-#ifndef EMSCRIPTEN // We provide this in JS
+#ifndef __EMSCRIPTEN__ // We provide this in JS
_LIBCPP_NORETURN
void
terminate() _NOEXCEPT
@@ -101,10 +101,10 @@ terminate() _NOEXCEPT
}
#endif // _LIBCPP_NO_EXCEPTIONS
}
-#endif // !EMSCRIPTEN
+#endif // !__EMSCRIPTEN__
#endif // !defined(LIBCXXRT) && !defined(_LIBCPPABI_VERSION)
-#if !defined(LIBCXXRT) && !defined(__GLIBCXX__) && !defined(EMSCRIPTEN)
+#if !defined(LIBCXXRT) && !defined(__GLIBCXX__) && !defined(__EMSCRIPTEN__)
bool uncaught_exception() _NOEXCEPT
{
#if defined(__APPLE__) || defined(_LIBCPPABI_VERSION)
diff --git a/system/lib/libcxx/locale.cpp b/system/lib/libcxx/locale.cpp
index d95d0c9c..ad64668f 100644
--- a/system/lib/libcxx/locale.cpp
+++ b/system/lib/libcxx/locale.cpp
@@ -792,7 +792,7 @@ ctype<wchar_t>::do_toupper(char_type c) const
{
#ifdef _LIBCPP_HAS_DEFAULTRUNELOCALE
return isascii(c) ? _DefaultRuneLocale.__mapupper[c] : c;
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
return isascii(c) ? ctype<char>::__classic_upper_table()[c] : c;
#else
return (isascii(c) && iswlower_l(c, __cloc())) ? c-L'a'+L'A' : c;
@@ -805,7 +805,7 @@ ctype<wchar_t>::do_toupper(char_type* low, const char_type* high) const
for (; low != high; ++low)
#ifdef _LIBCPP_HAS_DEFAULTRUNELOCALE
*low = isascii(*low) ? _DefaultRuneLocale.__mapupper[*low] : *low;
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
*low = isascii(*low) ? ctype<char>::__classic_upper_table()[*low]
: *low;
#else
@@ -819,7 +819,7 @@ ctype<wchar_t>::do_tolower(char_type c) const
{
#ifdef _LIBCPP_HAS_DEFAULTRUNELOCALE
return isascii(c) ? _DefaultRuneLocale.__maplower[c] : c;
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
return isascii(c) ? ctype<char>::__classic_lower_table()[c] : c;
#else
return (isascii(c) && isupper_l(c, __cloc())) ? c-L'A'+'a' : c;
@@ -832,7 +832,7 @@ ctype<wchar_t>::do_tolower(char_type* low, const char_type* high) const
for (; low != high; ++low)
#ifdef _LIBCPP_HAS_DEFAULTRUNELOCALE
*low = isascii(*low) ? _DefaultRuneLocale.__maplower[*low] : *low;
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
*low = isascii(*low) ? ctype<char>::__classic_lower_table()[*low]
: *low;
#else
@@ -901,7 +901,7 @@ ctype<char>::do_toupper(char_type c) const
static_cast<char>(_DefaultRuneLocale.__mapupper[static_cast<ptrdiff_t>(c)]) : c;
#elif defined(__NetBSD__)
return static_cast<char>(__classic_upper_table()[static_cast<unsigned char>(c)]);
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__)
return isascii(c) ?
static_cast<char>(__classic_upper_table()[static_cast<unsigned char>(c)]) : c;
#else
@@ -918,7 +918,7 @@ ctype<char>::do_toupper(char_type* low, const char_type* high) const
static_cast<char>(_DefaultRuneLocale.__mapupper[static_cast<ptrdiff_t>(*low)]) : *low;
#elif defined(__NetBSD__)
*low = static_cast<char>(__classic_upper_table()[static_cast<unsigned char>(*low)]);
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__)
*low = isascii(*low) ?
static_cast<char>(__classic_upper_table()[static_cast<size_t>(*low)]) : *low;
#else
@@ -935,7 +935,7 @@ ctype<char>::do_tolower(char_type c) const
static_cast<char>(_DefaultRuneLocale.__maplower[static_cast<ptrdiff_t>(c)]) : c;
#elif defined(__NetBSD__)
return static_cast<char>(__classic_lower_table()[static_cast<unsigned char>(c)]);
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
return isascii(c) ?
static_cast<char>(__classic_lower_table()[static_cast<size_t>(c)]) : c;
#else
@@ -951,7 +951,7 @@ ctype<char>::do_tolower(char_type* low, const char_type* high) const
*low = isascii(*low) ? static_cast<char>(_DefaultRuneLocale.__maplower[static_cast<ptrdiff_t>(*low)]) : *low;
#elif defined(__NetBSD__)
*low = static_cast<char>(__classic_lower_table()[static_cast<unsigned char>(*low)]);
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__)
*low = isascii(*low) ? static_cast<char>(__classic_lower_table()[static_cast<size_t>(*low)]) : *low;
#else
*low = (isascii(*low) && isupper_l(*low, __cloc())) ? *low-'A'+'a' : *low;
@@ -992,7 +992,7 @@ ctype<char>::do_narrow(const char_type* low, const char_type* high, char dfault,
return low;
}
-#ifdef EMSCRIPTEN
+#ifdef __EMSCRIPTEN__
extern "C" const unsigned short ** __ctype_b_loc();
extern "C" const int ** __ctype_tolower_loc();
extern "C" const int ** __ctype_toupper_loc();
@@ -1013,7 +1013,7 @@ ctype<char>::classic_table() _NOEXCEPT
return _ctype+1; // internal ctype mask table defined in msvcrt.dll
// This is assumed to be safe, which is a nonsense assumption because we're
// going to end up dereferencing it later...
-#elif defined(EMSCRIPTEN)
+#elif defined(__EMSCRIPTEN__)
return *__ctype_b_loc();
#else
// Platform not supported: abort so the person doing the port knows what to
@@ -1050,7 +1050,7 @@ ctype<char>::__classic_upper_table() _NOEXCEPT
return _C_toupper_tab_ + 1;
}
-#elif defined(EMSCRIPTEN)
+#elif defined(__EMSCRIPTEN__)
const int*
ctype<char>::__classic_lower_table() _NOEXCEPT
{
@@ -1062,7 +1062,7 @@ ctype<char>::__classic_upper_table() _NOEXCEPT
{
return *__ctype_toupper_loc();
}
-#endif // __GLIBC__ || EMSCRIPTEN || __NETBSD__
+#endif // __GLIBC__ || __EMSCRIPTEN__ || __NETBSD__
// template <> class ctype_byname<char>
diff --git a/system/lib/libcxxabi/src/cxa_new_delete.cpp b/system/lib/libcxxabi/src/cxa_new_delete.cpp
index e6fee5f2..7fd0b3b7 100644
--- a/system/lib/libcxxabi/src/cxa_new_delete.cpp
+++ b/system/lib/libcxxabi/src/cxa_new_delete.cpp
@@ -228,7 +228,7 @@ bad_array_new_length::what() const _NOEXCEPT
return "bad_array_new_length";
}
-#ifdef EMSCRIPTEN
+#ifdef __EMSCRIPTEN__
// We don't build the new.cpp from libcxx, so we need to define this.
void
__throw_bad_alloc()
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/filesystem/src.js b/tests/filesystem/src.js
index dbdd4bed..91337f5b 100644
--- a/tests/filesystem/src.js
+++ b/tests/filesystem/src.js
@@ -1,16 +1,18 @@
var dummy_device = FS.makedev(64, 0);
FS.registerDevice(dummy_device, {});
-FS.createFolder('/', 'forbidden', false, false);
-FS.createFolder('/forbidden', 'test', true, true);
-FS.createPath('/', 'abc/123', true, true);
-FS.createPath('/', 'abc/456', true, true);
-FS.createPath('/', 'def/789', true, true);
-FS.mkdev('/abc/deviceA', 0666, dummy_device);
-FS.mkdev('/def/deviceB', 0666, dummy_device);
-FS.createLink('/abc', 'localLink', '123', true, true);
-FS.createLink('/abc', 'rootLink', '/', true, true);
-FS.createLink('/abc', 'relativeLink', '../def', true, true);
+FS.mkdir('/forbidden', 0000);
+FS.mkdir('/forbidden/test');
+FS.mkdir('/abc');
+FS.mkdir('/abc/123');
+FS.mkdir('/abc/456');
+FS.mkdir('/def');
+FS.mkdir('/def/789');
+FS.mkdev('/abc/deviceA', dummy_device);
+FS.mkdev('/def/deviceB', dummy_device);
+FS.symlink('123', '/abc/localLink');
+FS.symlink('/', '/abc/rootLink');
+FS.symlink('../def', '/abc/relativeLink');
FS.ignorePermissions = false;
function explore(path) {
diff --git a/tests/gl_vertex_buffer.c b/tests/gl_vertex_buffer.c
new file mode 100644
index 00000000..6b695462
--- /dev/null
+++ b/tests/gl_vertex_buffer.c
@@ -0,0 +1,195 @@
+/*******************************************************************
+ * *
+ * Using SDL With OpenGL *
+ * *
+ * Tutorial by Kyle Foley (sdw) *
+ * *
+ * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL *
+ * *
+ *******************************************************************/
+
+/*
+ THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION
+ AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN.
+
+ THE ORIGINAL AUTHOR IS KYLE FOLEY.
+
+ THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY
+ OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
+ MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE,
+ ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
+ RESULTING FROM THE USE, MODIFICATION, OR
+ REDISTRIBUTION OF THIS SOFTWARE.
+ */
+
+#if !EMSCRIPTEN
+#define USE_GLEW 0
+#endif
+
+#if USE_GLEW
+#include "GL/glew.h"
+#endif
+
+#include <SDL/SDL.h>
+
+#if !USE_GLEW
+#include "SDL/SDL_opengl.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+int main(int argc, char *argv[])
+{
+ SDL_Surface *screen;
+
+ // Slightly different SDL initialization
+ if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) {
+ printf("Unable to initialize SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new*
+
+ screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed*
+ if ( !screen ) {
+ printf("Unable to set video mode: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ // Set the OpenGL state after creating the context with SDL_SetVideoMode
+
+ glClearColor( 0, 0, 0, 0 );
+
+#if !EMSCRIPTEN
+ glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL
+#endif
+
+ glViewport( 0, 0, 640, 480 );
+
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+
+ // Clear the screen before drawing
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ typedef struct Color {
+ GLubyte r;
+ GLubyte g;
+ GLubyte b;
+ GLubyte a;
+ } Color;
+
+ typedef struct Vertex {
+ GLfloat x;
+ GLfloat y;
+ Color color;
+ } Vertex;
+
+ Vertex vertices[18] = {
+ {-1.00, 0.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-1.00, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.75, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.75, 1.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ {-0.50, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.50, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.25, 0.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-0.25, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.00, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.00, 1.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 0.25, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.25, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.50, 0.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ { 0.50, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.75, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.75, 1.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 1.00, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 1.00, 1.0, {0xFF, 0xFF, 0x00, 0xFF}}
+ };
+
+ Vertex vertices2[18] = {
+ {-1.00, -1.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-1.00, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.75, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.75, 0.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ {-0.50, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.50, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.25, -1.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-0.25, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.00, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.00, 0.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 0.25, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.25, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.50, -1.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ { 0.50, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.75, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.75, 0.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 1.00, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 1.00, 0.0, {0xFF, 0xFF, 0x00, 0xFF}}
+ };
+
+ // make a vertex buffer for the second set of vertices
+ GLuint vbo = 0;
+ glGenBuffers(1, &vbo);
+
+
+ // bind to it
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ // send it to gl
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_READ); // GL_STATIC_READ is not in WebGL!
+
+ // unbind from it
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+
+ // DRAW
+
+ // Clear the screen before drawing
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ // This test ensures that we can use two separate arrays in memory for different
+ // attributes, and that they each can have different stride.
+ // The first test shows implicit striding (the zero indicates tightly packed)
+ // The second test shows explicit striding where the stride is passed in
+ // even though it also is tightly packed
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+
+ // TEST 1 - clientside data
+
+ glVertexPointer(2, GL_FLOAT, sizeof(Vertex), vertices);
+ glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &vertices[0].color);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);
+ glDrawArrays(GL_TRIANGLE_STRIP, 10, 3);
+
+
+
+ // TEST 2 - bind to array buffer, gl*Pointer calls are offsets into the buffer, which was previously uploaded to
+
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+
+ glVertexPointer(2, GL_FLOAT, sizeof(Vertex), 0);
+ glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), (GLvoid*)((GLvoid*)&vertices2[0].color - (GLvoid*)&vertices2[0]));
+
+// gldrawarrays first with a low number of vertices, then with a high number
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);
+ glDrawArrays(GL_TRIANGLE_STRIP, 10, 3);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ SDL_GL_SwapBuffers();
+
+#if !EMSCRIPTEN
+ // Wait for 3 seconds to give us a chance to see the image
+ SDL_Delay(3000);
+#endif
+
+ SDL_Quit();
+
+ return 0;
+}
diff --git a/tests/gl_vertex_buffer.png b/tests/gl_vertex_buffer.png
new file mode 100644
index 00000000..3e1f2230
--- /dev/null
+++ b/tests/gl_vertex_buffer.png
Binary files differ
diff --git a/tests/gl_vertex_buffer_pre.c b/tests/gl_vertex_buffer_pre.c
new file mode 100644
index 00000000..84b76569
--- /dev/null
+++ b/tests/gl_vertex_buffer_pre.c
@@ -0,0 +1,177 @@
+/*******************************************************************
+ * *
+ * Using SDL With OpenGL *
+ * *
+ * Tutorial by Kyle Foley (sdw) *
+ * *
+ * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL *
+ * *
+ *******************************************************************/
+
+/*
+ THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION
+ AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN.
+
+ THE ORIGINAL AUTHOR IS KYLE FOLEY.
+
+ THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY
+ OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
+ MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE,
+ ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
+ RESULTING FROM THE USE, MODIFICATION, OR
+ REDISTRIBUTION OF THIS SOFTWARE.
+ */
+
+#if !EMSCRIPTEN
+#define USE_GLEW 0
+#endif
+
+#if USE_GLEW
+#include "GL/glew.h"
+#endif
+
+#include <SDL/SDL.h>
+
+#if !USE_GLEW
+#include "SDL/SDL_opengl.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+int main(int argc, char *argv[])
+{
+ SDL_Surface *screen;
+
+ // Slightly different SDL initialization
+ if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) {
+ printf("Unable to initialize SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new*
+
+ screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed*
+ if ( !screen ) {
+ printf("Unable to set video mode: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ // Set the OpenGL state after creating the context with SDL_SetVideoMode
+
+ glClearColor( 0, 0, 0, 0 );
+
+#if !EMSCRIPTEN
+ glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL
+#endif
+
+ glViewport( 0, 0, 640, 480 );
+
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+
+ // Clear the screen before drawing
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ typedef struct Color {
+ GLubyte r;
+ GLubyte g;
+ GLubyte b;
+ GLubyte a;
+ } Color;
+
+ typedef struct Vertex {
+ GLfloat x;
+ GLfloat y;
+ Color color;
+ } Vertex;
+
+ Vertex vertices[18] = {
+ {-1.00, 0.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-1.00, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.75, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.75, 1.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ {-0.50, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.50, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.25, 0.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-0.25, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.00, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.00, 1.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 0.25, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.25, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.50, 0.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ { 0.50, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.75, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.75, 1.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 1.00, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 1.00, 1.0, {0xFF, 0xFF, 0x00, 0xFF}}
+ };
+
+ Vertex vertices2[18] = {
+ {-1.00, -1.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-1.00, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.75, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.75, 0.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ {-0.50, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.50, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.25, -1.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-0.25, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.00, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.00, 0.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 0.25, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.25, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.50, -1.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ { 0.50, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.75, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.75, 0.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 1.00, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 1.00, 0.0, {0xFF, 0xFF, 0x00, 0xFF}}
+ };
+
+ // make a vertex buffer for the second set of vertices
+ GLuint vbo = 0;
+ glGenBuffers(1, &vbo);
+
+
+ // bind to it
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ // send it to gl
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW);
+
+ // unbind from it
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+
+ // DRAW
+
+ // Clear the screen before drawing
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ // This test ensures that we can use two separate arrays in memory for different
+ // attributes, and that they each can have different stride.
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+
+ glVertexPointer(2, GL_FLOAT, sizeof(Vertex), vertices);
+ glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &vertices[0].color);
+ glDrawArrays(GL_TRIANGLE_STRIP, 10, 3);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ SDL_GL_SwapBuffers();
+
+#if !EMSCRIPTEN
+ // Wait for 3 seconds to give us a chance to see the image
+ SDL_Delay(3000);
+#endif
+
+ SDL_Quit();
+
+ return 0;
+}
diff --git a/tests/gl_vertex_buffer_pre.png b/tests/gl_vertex_buffer_pre.png
new file mode 100644
index 00000000..5677a868
--- /dev/null
+++ b/tests/gl_vertex_buffer_pre.png
Binary files differ
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..8747c340 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):
@@ -250,7 +254,7 @@ process(sys.argv[1])
os.chdir(cwd)
out = open(stdout, 'r').read()
err = open(stderr, 'r').read()
- if engine == SPIDERMONKEY_ENGINE and Settings.ASM_JS:
+ if engine == SPIDERMONKEY_ENGINE and Settings.ASM_JS == 1:
err = self.validate_asmjs(err)
if output_nicerizer:
ret = output_nicerizer(out, err)
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/test_browser.py b/tests/test_browser.py
index a0c4dceb..3ac640f1 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -1207,6 +1207,12 @@ If manually bisecting:
def test_gl_stride(self):
self.btest('gl_stride.c', reference='gl_stride.png', args=['-s', 'GL_UNSAFE_OPTS=0', '-s', 'LEGACY_GL_EMULATION=1'])
+ def test_gl_vertex_buffer_pre(self):
+ self.btest('gl_vertex_buffer_pre.c', reference='gl_vertex_buffer_pre.png', args=['-s', 'GL_UNSAFE_OPTS=0', '-s', 'LEGACY_GL_EMULATION=1'])
+
+ def test_gl_vertex_buffer(self):
+ self.btest('gl_vertex_buffer.c', reference='gl_vertex_buffer.png', args=['-s', 'GL_UNSAFE_OPTS=0', '-s', 'LEGACY_GL_EMULATION=1'], reference_slack=1)
+
def test_matrix_identity(self):
self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840'], args=['-s', 'LEGACY_GL_EMULATION=1'])
@@ -1404,3 +1410,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 6d15da1e..5d4f35e8 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -3742,7 +3742,8 @@ def process(filename):
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')
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
src = r'''
#include <stdio.h>
@@ -3762,7 +3763,8 @@ def process(filename):
self.do_run(src, 'Inline JS is very cool\n3.64\n')
def test_inlinejs2(self):
- if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm')
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
src = r'''
#include <stdio.h>
@@ -3773,8 +3775,8 @@ def process(filename):
}
void mult() {
- asm("var $_$1 = Math.abs(-100); $_$1 *= 2;"); // multiline
- asm __volatile__("Module.print($_$1); Module.print('\n')");
+ asm("var $_$1 = Math.abs(-100); $_$1 *= 2; Module.print($_$1)"); // multiline
+ asm __volatile__("Module.print('done')");
}
int main(int argc, char **argv) {
@@ -3784,7 +3786,7 @@ def process(filename):
}
'''
- self.do_run(src, '4\n200\n')
+ self.do_run(src, '4\n200\ndone\n')
def test_inlinejs3(self):
if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm')
@@ -5580,11 +5582,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>
@@ -5599,10 +5643,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>
@@ -5621,28 +5665,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;
@@ -5660,11 +5693,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>
@@ -5686,6 +5719,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");
@@ -5697,14 +5737,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++) {
@@ -5715,27 +5747,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)
+ if '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>
@@ -5760,21 +5787,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");
}
@@ -5818,23 +5847,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
@@ -5876,29 +5896,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() {
@@ -5907,15 +5921,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;
@@ -5933,24 +5948,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')
@@ -5959,14 +5966,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");
@@ -5987,15 +5995,372 @@ 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_dlfcn_longjmp(self):
+ if not self.can_dlfcn(): return
+
+ self.prep_dlfcn_lib()
+ lib_src = r'''
+ #include <setjmp.h>
+
+ void jumpy(jmp_buf buf) {
+ static int i = 0;
+ i++;
+ if (i == 10) longjmp(buf, i);
+ printf("pre %d\n", i);
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_jumpy']
+ 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>
+ #include <setjmp.h>
+
+ typedef void (*jumpfunc)(jmp_buf);
+
+ int main() {
+ printf("go!\n");
+
+ void *lib_handle;
+ lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle != NULL);
+
+ jumpfunc jumpy = (jumpfunc)dlsym(lib_handle, "jumpy");
+ assert(jumpy);
+
+ jmp_buf buf;
+ int jmpval = setjmp(buf);
+ if (jmpval == 0) {
+ while (1) jumpy(buf);
+ } else {
+ printf("out!\n");
+ }
+
+ return 0;
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free']
+ self.do_run(src, '''go!
+pre 1
+pre 2
+pre 3
+pre 4
+pre 5
+pre 6
+pre 7
+pre 8
+pre 9
+out!
+''', post_build=self.dlfcn_post_build, force_c=True)
+
+ def zzztest_dlfcn_exceptions(self): # TODO: make this work. need to forward tempRet0 across modules
+ if not self.can_dlfcn(): return
+
+ Settings.DISABLE_EXCEPTION_CATCHING = 0
+
+ self.prep_dlfcn_lib()
+ lib_src = r'''
+ extern "C" {
+ int ok() {
+ return 65;
+ }
+ int fail() {
+ throw 123;
+ }
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_ok', '_fail']
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.cpp')
+ 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 int (*intfunc)();
+
+ int main() {
+ printf("go!\n");
+
+ void *lib_handle;
+ lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle != NULL);
+
+ intfunc okk = (intfunc)dlsym(lib_handle, "ok");
+ intfunc faill = (intfunc)dlsym(lib_handle, "fail");
+ assert(okk && faill);
+
+ try {
+ printf("ok: %d\n", okk());
+ } catch(...) {
+ printf("wha\n");
+ }
+
+ try {
+ printf("fail: %d\n", faill());
+ } catch(int x) {
+ printf("int %d\n", x);
+ }
+
+ try {
+ printf("fail: %d\n", faill());
+ } catch(double x) {
+ printf("caught %f\n", x);
+ }
+
+ return 0;
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free']
+ self.do_run(src, '''go!
+ok: 65
+int 123
+ok
+''', post_build=self.dlfcn_post_build)
+
def test_rand(self):
return self.skip('rand() is now random') # FIXME
@@ -6992,7 +7357,7 @@ def process(filename):
FS.registerDevice(dummy_device, {});
FS.createDataFile('/', 'file', 'abcdef', true, true);
- FS.mkdev('/device', 0666, dummy_device);
+ FS.mkdev('/device', dummy_device);
\'\'\'
)
open(filename, 'w').write(src)
@@ -7250,32 +7615,18 @@ def process(filename):
Settings.INCLUDE_FULL_LIBRARY = 0
def test_unistd_access(self):
- add_pre_run = '''
-def process(filename):
- import tools.shared as shared
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(shared.path_from_root('tests', 'unistd', 'access.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, expected)
def test_unistd_curdir(self):
- add_pre_run = '''
-def process(filename):
- import tools.shared as shared
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(shared.path_from_root('tests', 'unistd', 'curdir.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
src = open(path_from_root('tests', 'unistd', 'curdir.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'curdir.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, expected)
def test_unistd_close(self):
src = open(path_from_root('tests', 'unistd', 'close.c'), 'r').read()
@@ -7302,18 +7653,11 @@ def process(filename):
self.do_run(src, expected)
def test_unistd_truncate(self):
- add_pre_run = '''
-def process(filename):
- import tools.shared as shared
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(shared.path_from_root('tests', 'unistd', 'truncate.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'truncate.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, expected)
def test_unistd_swab(self):
src = open(path_from_root('tests', 'unistd', 'swab.c'), 'r').read()
@@ -7339,18 +7683,11 @@ def process(filename):
self.do_run(src, 'success', force_c=True)
def test_unistd_links(self):
- add_pre_run = '''
-def process(filename):
- import tools.shared as shared
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(shared.path_from_root('tests', 'unistd', 'links.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
src = open(path_from_root('tests', 'unistd', 'links.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'links.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, expected)
def test_unistd_sleep(self):
src = open(path_from_root('tests', 'unistd', 'sleep.c'), 'r').read()
@@ -7358,18 +7695,12 @@ def process(filename):
self.do_run(src, expected)
def test_unistd_io(self):
- add_pre_run = '''
-def process(filename):
- import tools.shared as shared
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(shared.path_from_root('tests', 'unistd', 'io.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
+ if self.run_name == 'o2': return self.skip('non-asm optimized builds can fail with inline js')
src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'io.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, expected)
def test_unistd_misc(self):
src = open(path_from_root('tests', 'unistd', 'misc.c'), 'r').read()
@@ -7439,168 +7770,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 fd1a6245..c6f5c333 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -1884,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/tests/unistd/access.c b/tests/unistd/access.c
index 89428610..4d5ba08e 100644
--- a/tests/unistd/access.c
+++ b/tests/unistd/access.c
@@ -1,8 +1,16 @@
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
+#include <emscripten.h>
int main() {
+ EM_ASM(
+ FS.writeFile('/forbidden', ''); FS.chmod('/forbidden', 0000);
+ FS.writeFile('/readable', ''); FS.chmod('/readable', 0444);
+ FS.writeFile('/writeable', ''); FS.chmod('/writeable', 0222);
+ FS.writeFile('/allaccess', ''); FS.chmod('/allaccess', 0777);
+ );
+
char* files[] = {"/readable", "/writeable",
"/allaccess", "/forbidden", "/nonexistent"};
for (int i = 0; i < sizeof files / sizeof files[0]; i++) {
diff --git a/tests/unistd/access.js b/tests/unistd/access.js
deleted file mode 100644
index ea9e6359..00000000
--- a/tests/unistd/access.js
+++ /dev/null
@@ -1,4 +0,0 @@
-FS.createDataFile('/', 'forbidden', '', false, false);
-FS.createDataFile('/', 'readable', '', true, false);
-FS.createDataFile('/', 'writeable', '', false, true);
-FS.createDataFile('/', 'allaccess', '', true, true);
diff --git a/tests/unistd/access.out b/tests/unistd/access.out
index dffe0b9e..d462e5a5 100644
--- a/tests/unistd/access.out
+++ b/tests/unistd/access.out
@@ -2,8 +2,8 @@ F_OK(/readable): 0
errno: 0
R_OK(/readable): 0
errno: 0
-X_OK(/readable): 0
-errno: 0
+X_OK(/readable): -1
+errno: 13
W_OK(/readable): -1
errno: 13
diff --git a/tests/unistd/curdir.c b/tests/unistd/curdir.c
index 63b9c7fe..b9f22dd7 100644
--- a/tests/unistd/curdir.c
+++ b/tests/unistd/curdir.c
@@ -2,8 +2,19 @@
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
+#include <emscripten.h>
int main() {
+ EM_ASM(
+ var dummy_device = FS.makedev(64, 0);
+ FS.registerDevice(dummy_device, {});
+ FS.mkdev('/device', dummy_device);
+
+ FS.mkdir('/folder');
+ FS.symlink('/folder', '/link');
+ FS.writeFile('/file', '', { mode: 0777 });
+ );
+
char buffer[256];
printf("getwd: %s\n", getwd(buffer));
printf("errno: %d\n", errno);
diff --git a/tests/unistd/curdir.js b/tests/unistd/curdir.js
deleted file mode 100644
index 75a1d2ce..00000000
--- a/tests/unistd/curdir.js
+++ /dev/null
@@ -1,7 +0,0 @@
-var dummy_device = FS.makedev(64, 0);
-FS.registerDevice(dummy_device, {});
-
-FS.createDataFile('/', 'file', '', true, true);
-FS.createFolder('/', 'folder', true, true);
-FS.mkdev('/device', 0666, dummy_device);
-FS.createLink('/', 'link', 'folder', true, true);
diff --git a/tests/unistd/io.c b/tests/unistd/io.c
index a96290ef..0ff5f4fb 100644
--- a/tests/unistd/io.c
+++ b/tests/unistd/io.c
@@ -3,8 +3,58 @@
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
+#include <emscripten.h>
int main() {
+ EM_ASM(
+ var major = 80;
+
+ var device = FS.makedev(major++, 0);
+ FS.registerDevice(device, {
+ open: function(stream) {
+ stream.payload = [65, 66, 67, 68];
+ },
+ read: function(stream, buffer, offset, length, pos) {
+ var bytesRead = 0;
+ for (var i = 0; i < length; i++) {
+ if (stream.payload.length) {
+ bytesRead++;
+ buffer[offset+i] = stream.payload.shift();
+ } else {
+ break;
+ }
+ }
+ return bytesRead;
+ },
+ write: function(stream, buffer, offset, length, pos) {
+ for (var i = 0; i < length; i++) {
+ Module.print('TO DEVICE: ' + buffer[offset+i]);
+ }
+ return i;
+ }
+ });
+ FS.mkdev('/device', device);
+
+ var broken_device = FS.makedev(major++, 0);
+ FS.registerDevice(broken_device, {
+ read: function(stream, buffer, offset, length, pos) {
+ throw new FS.ErrnoError(ERRNO_CODES.EIO);
+ },
+ write: function(stream, buffer, offset, length, pos) {
+ throw new FS.ErrnoError(ERRNO_CODES.EIO);
+ }
+ });
+ FS.mkdev('/broken-device', broken_device);
+
+ // NB: These are meant to test FS.createDevice specifically,
+ // and as such do not use registerDevice/mkdev
+ FS.createDevice('/', 'createDevice-read-only', function() {});
+ FS.createDevice('/', 'createDevice-write-only', null, function() {});
+
+ FS.mkdir('/folder', 0777);
+ FS.writeFile('/file', '1234567890');
+ );
+
char readBuffer[256] = {0};
char writeBuffer[] = "writeme";
diff --git a/tests/unistd/io.js b/tests/unistd/io.js
deleted file mode 100644
index 11c0da79..00000000
--- a/tests/unistd/io.js
+++ /dev/null
@@ -1,52 +0,0 @@
-(function() {
- var major = 80;
-
- var device = FS.makedev(major++, 0);
- var device_ops = {
- open: function(stream) {
- stream.payload = [65, 66, 67, 68];
- },
- read: function(stream, buffer, offset, length, pos) {
- var bytesRead = 0;
- for (var i = 0; i < length; i++) {
- if (stream.payload.length) {
- bytesRead++;
- buffer[offset+i] = stream.payload.shift();
- } else {
- break;
- }
- }
- return bytesRead;
- },
- write: function(stream, buffer, offset, length, pos) {
- for (var i = 0; i < length; i++) {
- Module.print("TO DEVICE: " + buffer[offset+i]);
- }
- return i;
- }
- };
- FS.registerDevice(device, device_ops);
-
- FS.mkdev('/device', 0666, device);
-
- var broken_device = FS.makedev(major++, 0);
- var broken_device_ops = {
- read: function(stream, buffer, offset, length, pos) {
- throw new FS.ErrnoError(ERRNO_CODES.EIO);
- },
- write: function(stream, buffer, offset, length, pos) {
- throw new FS.ErrnoError(ERRNO_CODES.EIO);
- }
- };
- FS.registerDevice(broken_device, broken_device_ops);
-
- FS.mkdev('/broken-device', 0666, broken_device);
-
- // NB: These are meant to test FS.createDevice specifically,
- // and as such do not use registerDevice/mkdev
- FS.createDevice('/', 'createDevice-read-only', function() {});
- FS.createDevice('/', 'createDevice-write-only', null, function() {});
-
- FS.createDataFile('/', 'file', '1234567890', true, true);
- FS.createFolder('/', 'folder', true, true);
-})();
diff --git a/tests/unistd/links.c b/tests/unistd/links.c
index c6da83b9..5b403c1f 100644
--- a/tests/unistd/links.c
+++ b/tests/unistd/links.c
@@ -1,8 +1,15 @@
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
+#include <emscripten.h>
int main() {
+ EM_ASM(
+ FS.symlink('../test/../there!', '/link');
+ FS.writeFile('/file', 'test');
+ FS.mkdir('/folder');
+ );
+
char* files[] = {"/link", "/file", "/folder"};
char buffer[256] = {0};
diff --git a/tests/unistd/links.js b/tests/unistd/links.js
deleted file mode 100644
index 5e58a729..00000000
--- a/tests/unistd/links.js
+++ /dev/null
@@ -1,3 +0,0 @@
-FS.createLink('/', 'link', '../test/../there!', true, true);
-FS.createDataFile('/', 'file', 'test', true, true);
-FS.createFolder('/', 'folder', true, true);
diff --git a/tests/unistd/truncate.c b/tests/unistd/truncate.c
index 18920976..b1d9fc96 100644
--- a/tests/unistd/truncate.c
+++ b/tests/unistd/truncate.c
@@ -4,8 +4,15 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
+#include <emscripten.h>
int main() {
+ EM_ASM(
+ FS.writeFile('/towrite', 'abcdef');
+ FS.writeFile('/toread', 'abcdef');
+ FS.chmod('/toread', 0444);
+ );
+
struct stat s;
int f = open("/towrite", O_WRONLY);
int f2 = open("/toread", O_RDONLY);
diff --git a/tests/unistd/truncate.js b/tests/unistd/truncate.js
deleted file mode 100644
index 6a4c6868..00000000
--- a/tests/unistd/truncate.js
+++ /dev/null
@@ -1,2 +0,0 @@
-FS.createDataFile('/', 'towrite', 'abcdef', true, true);
-FS.createDataFile('/', 'toread', 'abcdef', true, false);
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index b42164f9..788a76ed 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -1564,6 +1564,7 @@ function normalizeAsm(func) {
var data = {
params: {}, // ident => ASM_* type
vars: {}, // ident => ASM_* type
+ inlines: [], // list of inline assembly copies
};
// process initial params
var stats = func[3];
@@ -1621,6 +1622,10 @@ function normalizeAsm(func) {
node[0] = 'name';
node[1] = 'Math_' + node[2];
}
+ } else if (type === 'call' && node[1][0] === 'function') {
+ assert(!node[1][1]); // anonymous functions only
+ data.inlines.push(node[1]);
+ node[1] = ['name', 'inlinejs']; // empty out body, leave arguments, so they are eliminated/minified properly
}
});
i++;
@@ -1669,6 +1674,14 @@ function denormalizeAsm(func, data) {
} else {
stats[next] = emptyNode();
}
+ if (data.inlines.length > 0) {
+ var i = 0;
+ traverse(func, function(node, type) {
+ if (type === 'call' && node[1][0] === 'name' && node[1][1] === 'inlinejs') {
+ node[1] = data.inlines[i++]; // swap back in the body
+ }
+ });
+ }
//printErr('denormalized \n\n' + astToSrc(func) + '\n\n');
}
@@ -2019,6 +2032,7 @@ function registerize(ast) {
var finalAsmData = {
params: {},
vars: {},
+ inlines: asmData.inlines,
};
for (var i = 1; i < nextReg; i++) {
var reg = fullNames[i];
@@ -3452,7 +3466,7 @@ function outline(ast) {
});
// finalize
var newFunc = ['defun', newIdent, ['sp'], code];
- var newAsmData = { params: { sp: ASM_INT }, vars: {} };
+ var newAsmData = { params: { sp: ASM_INT }, vars: {}, inlines: asmData.inlines };
for (var v in codeInfo.reads) {
if (v != 'sp') newAsmData.vars[v] = getAsmType(v, asmData);
}
diff --git a/tools/jsrun.py b/tools/jsrun.py
index 91038f6e..6f77ce51 100644
--- a/tools/jsrun.py
+++ b/tools/jsrun.py
@@ -10,6 +10,7 @@ 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, stdin=None, stdout=PIPE, stderr=None, cwd=None, full_output=False):
diff --git a/tools/shared.py b/tools/shared.py
index 3ee5db23..2e11d736 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -283,6 +283,20 @@ def check_node_version():
logging.warning('cannot check node version: %s', e)
return False
+# Finds the system temp directory without resorting to using the one configured in .emscripten
+def find_temp_directory():
+ if WINDOWS:
+ if os.getenv('TEMP') and os.path.isdir(os.getenv('TEMP')):
+ return os.getenv('TEMP')
+ elif os.getenv('TMP') and os.path.isdir(os.getenv('TMP')):
+ return os.getenv('TMP')
+ elif os.path.isdir('C:\\temp'):
+ return os.getenv('C:\\temp')
+ else:
+ return None # No luck!
+ else:
+ return '/tmp'
+
# Check that basic stuff we need (a JS engine to compile, Node.js, and Clang and LLVM)
# exists.
# The test runner always does this check (through |force|). emcc does this less frequently,
@@ -429,7 +443,6 @@ EMCC = path_from_root('emcc')
EMXX = path_from_root('em++')
EMAR = path_from_root('emar')
EMRANLIB = path_from_root('emranlib')
-EMLIBTOOL = path_from_root('emlibtool')
EMCONFIG = path_from_root('em-config')
EMLINK = path_from_root('emlink.py')
EMMAKEN = path_from_root('tools', 'emmaken.py')
@@ -451,8 +464,13 @@ class Configuration:
try:
self.TEMP_DIR = TEMP_DIR
except NameError:
- logging.debug('TEMP_DIR not defined in ~/.emscripten, using /tmp')
- self.TEMP_DIR = '/tmp'
+ self.TEMP_DIR = find_temp_directory()
+ if self.TEMP_DIR == None:
+ logging.critical('TEMP_DIR not defined in ' + os.path.expanduser('~\\.emscripten') + ", and could not detect a suitable directory! Please configure .emscripten to contain a variable TEMP_DIR='/path/to/temp/dir'.")
+ logging.debug('TEMP_DIR not defined in ~/.emscripten, using ' + self.TEMP_DIR)
+
+ if not os.path.isdir(self.TEMP_DIR):
+ logging.critical("The temp directory TEMP_DIR='" + self.TEMP_DIR + "' doesn't seem to exist! Please make sure that the path is correct.")
self.CANONICAL_TEMP_DIR = os.path.join(self.TEMP_DIR, 'emscripten_temp')
@@ -470,12 +488,13 @@ class Configuration:
save_debug_files=os.environ.get('EMCC_DEBUG_SAVE'))
def apply_configuration():
- global configuration, DEBUG, EMSCRIPTEN_TEMP_DIR, DEBUG_CACHE, CANONICAL_TEMP_DIR
+ global configuration, DEBUG, EMSCRIPTEN_TEMP_DIR, DEBUG_CACHE, CANONICAL_TEMP_DIR, TEMP_DIR
configuration = Configuration()
DEBUG = configuration.DEBUG
EMSCRIPTEN_TEMP_DIR = configuration.EMSCRIPTEN_TEMP_DIR
DEBUG_CACHE = configuration.DEBUG_CACHE
CANONICAL_TEMP_DIR = configuration.CANONICAL_TEMP_DIR
+ TEMP_DIR = configuration.TEMP_DIR
apply_configuration()
logging.basicConfig(format='%(levelname)-8s %(name)s: %(message)s')
@@ -717,6 +736,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
@@ -782,7 +805,6 @@ class Building:
env['LD'] = EMCC if not WINDOWS else 'python %r' % EMCC
env['LDSHARED'] = EMCC if not WINDOWS else 'python %r' % EMCC
env['RANLIB'] = EMRANLIB if not WINDOWS else 'python %r' % EMRANLIB
- #env['LIBTOOL'] = EMLIBTOOL if not WINDOWS else 'python %r' % EMLIBTOOL
env['EMMAKEN_COMPILER'] = Building.COMPILER
env['EMSCRIPTEN_TOOLS'] = path_from_root('tools')
env['CFLAGS'] = env['EMMAKEN_CFLAGS'] = ' '.join(Building.COMPILER_TEST_OPTS)
@@ -843,6 +865,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 +916,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 +935,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 +948,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 +1201,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 +1449,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 +1461,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 +1495,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