summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS2
-rwxr-xr-xemcc21
-rwxr-xr-xemscripten.py10
-rw-r--r--src/jsifier.js4
-rw-r--r--src/library.js330
-rw-r--r--src/library_browser.js6
-rw-r--r--src/library_gl.js70
-rw-r--r--src/library_sdl.js236
-rw-r--r--src/parseTools.js4
-rw-r--r--system/include/SDL/SDL_keycode.h3
-rw-r--r--system/include/libc/sys/dirent.h1
-rw-r--r--tests/cases/zeroextarg.ll22
-rw-r--r--tests/dirent/test_readdir.c132
-rw-r--r--tests/fcntl-misc/src.c2
-rw-r--r--tests/fcntl-open/output.txt10
-rw-r--r--tests/fcntl-open/src.c64
-rwxr-xr-xtests/runner.py486
-rw-r--r--tests/sdl_gfx_primitives.c46
-rw-r--r--tests/sdl_gfx_primitives.pngbin0 -> 2357 bytes
-rw-r--r--tests/sdl_rotozoom.c20
-rw-r--r--tests/sdl_rotozoom.pngbin360054 -> 437956 bytes
-rw-r--r--tests/unistd/isatty.c38
-rw-r--r--tests/unistd/isatty.js5
-rw-r--r--tests/unistd/isatty.out10
-rw-r--r--tests/unistd/ttyname.c76
-rw-r--r--tests/unistd/ttyname.js1
-rw-r--r--tests/unistd/ttyname.out5
-rw-r--r--tests/utime/test_utime.c53
-rw-r--r--tools/js-optimizer.js193
-rw-r--r--tools/shared.py6
-rw-r--r--tools/test-js-optimizer-asm-last-output.js1
-rw-r--r--tools/test-js-optimizer-asm-last.js1
-rw-r--r--tools/test-js-optimizer-asm-pre-output.js3
-rw-r--r--tools/test-js-optimizer-asm-pre.js3
34 files changed, 1343 insertions, 521 deletions
diff --git a/AUTHORS b/AUTHORS
index 229a9cad..021240e2 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -86,3 +86,5 @@ a license to everyone to use it as detailed in LICENSE.)
* David Barksdale <david.barksdale@adcedosolutions.com>
* Manfred Manik Nerurkar <nerurkar*at*made-apps.biz> (copyright owned by MADE, GmbH)
* Joseph Gentle <me@josephg.com>
+* Douglas T. Crosher <dtc-moz@scieneer.com> (copyright owned by Mozilla Founcation)
+* Soeren Balko <soeren.balko@gmail.com>
diff --git a/emcc b/emcc
index 90a2ea71..bffe8d5d 100755
--- a/emcc
+++ b/emcc
@@ -522,14 +522,14 @@ if CONFIGURE_CONFIG or CMAKE_CONFIG:
open(tempout, 'w').write('//\n')
src = None
- for i in range(len(sys.argv)):
- if sys.argv[i].endswith('.c'):
+ for arg in sys.argv:
+ if arg.endswith('.c'):
try:
- src = open(sys.argv[i]).read()
- if debug_configure: open(tempout, 'a').write('============= ' + sys.argv[i] + '\n' + src + '\n=============\n\n')
+ src = open(arg).read()
+ if debug_configure: open(tempout, 'a').write('============= ' + arg + '\n' + src + '\n=============\n\n')
except:
pass
- if sys.argv[i].endswith('.s'):
+ if arg.endswith('.s'):
if debug_configure: open(tempout, 'a').write('(compiling .s assembly, must use clang\n')
use_js = 0
@@ -826,7 +826,7 @@ try:
newargs[i] = ''
newargs[i+1] = ''
elif newargs[i].startswith('--use-preload-cache'):
- use_preload_cache = True;
+ use_preload_cache = True
newargs[i] = ''
elif newargs[i] == '--ignore-dynamic-linking':
ignore_dynamic_linking = True
@@ -952,7 +952,7 @@ try:
if not prefix: continue
if l.startswith(prefix):
l = l[len(prefix):]
- break;
+ break
libs.append(l)
newargs[i] = ''
else:
@@ -1121,9 +1121,8 @@ try:
# Optimize source files
if llvm_opts > 0:
- for i in range(len(input_files)):
- input_file = input_files[i]
- if input_files[i].endswith(SOURCE_SUFFIXES):
+ for i, input_file in enumerate(input_files):
+ if input_file.endswith(SOURCE_SUFFIXES):
temp_file = temp_files[i]
logging.debug('optimizing %s with -O%d' % (input_file, llvm_opts))
shared.Building.llvm_opt(temp_file, llvm_opts)
@@ -1214,7 +1213,7 @@ try:
os.path.join('libc', 'gen', 'vwarn.c'),
os.path.join('libc', 'gen', 'vwarnx.c'),
os.path.join('libc', 'stdlib', 'strtod.c'),
- ];
+ ]
return build_libc('libc.bc', libc_files)
def apply_libc(need):
diff --git a/emscripten.py b/emscripten.py
index be54b035..9a2abb9a 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -168,10 +168,10 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
if DEBUG_CACHE and not out:
dfpath = os.path.join(get_configuration().TEMP_DIR, "ems_" + shortkey)
dfp = open(dfpath, 'w')
- dfp.write(pre_input);
- dfp.write("\n\n========================== settings_text\n\n");
- dfp.write(settings_text);
- dfp.write("\n\n========================== libraries\n\n");
+ dfp.write(pre_input)
+ dfp.write("\n\n========================== settings_text\n\n")
+ dfp.write(settings_text)
+ dfp.write("\n\n========================== libraries\n\n")
dfp.write("\n".join(libraries))
dfp.close()
print >>sys.stderr, ' cache miss, key data dumped to %s' % dfpath
@@ -490,7 +490,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
global_vars = map(lambda g: g['name'], filter(lambda g: settings['NAMED_GLOBALS'] or g.get('external') or g.get('unIndexable'), forwarded_json['Variables']['globals'].values()))
global_funcs = ['_' + key for key, value in forwarded_json['Functions']['libraryFunctions'].iteritems() if value != 2]
def math_fix(g):
- return g if not g.startswith('Math_') else g.split('_')[1];
+ return g if not g.startswith('Math_') else g.split('_')[1]
asm_global_funcs = ''.join([' var ' + g.replace('.', '_') + '=global.' + g + ';\n' for g in maths]) + \
''.join([' var ' + g + '=env.' + math_fix(g) + ';\n' for g in basic_funcs + global_funcs])
asm_global_vars = ''.join([' var ' + g + '=env.' + g + '|0;\n' for g in basic_vars + global_vars]) + \
diff --git a/src/jsifier.js b/src/jsifier.js
index 86931f3e..82b78d0a 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1479,8 +1479,8 @@ function JSify(data, functionsOnly, givenFunctions) {
return inline.apply(null, args); // Warning: inlining does not prevent recalculation of the arguments. They should be simple identifiers
}
- if (ASM_JS) {
- // remove unneeded arguments, which the asm sig can show us. this lets us alias memset with llvm.memset, we just
+ if (ASM_JS && ident.indexOf('llvm_') >= 0) {
+ // remove unneeded arguments in llvm intrinsic functions, which the asm sig can show us. this lets us alias memset with llvm.memset, we just
// drop the final 2 args so things validate properly in asm
var libsig = LibraryManager.library[simpleIdent + '__sig'];
if (libsig) {
diff --git a/src/library.js b/src/library.js
index bf969fd7..24e35178 100644
--- a/src/library.js
+++ b/src/library.js
@@ -588,8 +588,11 @@ LibraryManager.library = {
// Create the I/O devices.
var devFolder = FS.createFolder('/', 'dev', true, true);
var stdin = FS.createDevice(devFolder, 'stdin', input);
+ stdin.isTerminal = !stdinOverridden;
var stdout = FS.createDevice(devFolder, 'stdout', null, output);
+ stdout.isTerminal = !stdoutOverridden;
var stderr = FS.createDevice(devFolder, 'stderr', null, error);
+ stderr.isTerminal = !stderrOverridden;
FS.createDevice(devFolder, 'tty', input, output);
FS.createDevice(devFolder, 'null', function(){}, function(){});
@@ -601,7 +604,6 @@ LibraryManager.library = {
isRead: true,
isWrite: false,
isAppend: false,
- isTerminal: !stdinOverridden,
error: false,
eof: false,
ungotten: []
@@ -613,7 +615,6 @@ LibraryManager.library = {
isRead: false,
isWrite: true,
isAppend: false,
- isTerminal: !stdoutOverridden,
error: false,
eof: false,
ungotten: []
@@ -625,7 +626,6 @@ LibraryManager.library = {
isRead: false,
isWrite: true,
isAppend: false,
- isTerminal: !stderrOverridden,
error: false,
eof: false,
ungotten: []
@@ -737,7 +737,8 @@ LibraryManager.library = {
// int closedir(DIR *dirp);
// http://pubs.opengroup.org/onlinepubs/007908799/xsh/closedir.html
if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) {
- return ___setErrNo(ERRNO_CODES.EBADF);
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
} else {
_free(FS.streams[dirp].currentEntry);
FS.streams[dirp] = null;
@@ -749,7 +750,8 @@ LibraryManager.library = {
// long int telldir(DIR *dirp);
// http://pubs.opengroup.org/onlinepubs/007908799/xsh/telldir.html
if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) {
- return ___setErrNo(ERRNO_CODES.EBADF);
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
} else {
return FS.streams[dirp].position;
}
@@ -865,10 +867,6 @@ LibraryManager.library = {
}
var file = FS.findObject(Pointer_stringify(path));
if (file === null) return -1;
- if (!file.write) {
- ___setErrNo(ERRNO_CODES.EPERM);
- return -1;
- }
file.timestamp = time;
return 0;
},
@@ -1213,7 +1211,7 @@ LibraryManager.library = {
___setErrNo(ERRNO_CODES.EEXIST);
return -1;
}
- if ((isWrite || isCreate || isTruncate) && target.isFolder) {
+ if ((isWrite || isTruncate) && target.isFolder) {
___setErrNo(ERRNO_CODES.EISDIR);
return -1;
}
@@ -1381,7 +1379,7 @@ LibraryManager.library = {
posix_fallocate: function(fd, offset, len) {
// int posix_fallocate(int fd, off_t offset, off_t len);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_fallocate.html
- if (!FS.streams[fd] || FS.streams[fd].link ||
+ if (!FS.streams[fd] || !FS.streams[fd].isWrite || FS.streams[fd].link ||
FS.streams[fd].isFolder || FS.streams[fd].isDevice) {
___setErrNo(ERRNO_CODES.EBADF);
return -1;
@@ -1688,13 +1686,16 @@ LibraryManager.library = {
isatty: function(fildes) {
// int isatty(int fildes);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/isatty.html
- if (!FS.streams[fildes]) {
+ var stream = FS.streams[fildes];
+ if (!stream) {
___setErrNo(ERRNO_CODES.EBADF);
return 0;
}
- if (FS.streams[fildes].isTerminal) return 1;
- ___setErrNo(ERRNO_CODES.ENOTTY);
- return 0;
+ if (!stream.object.isTerminal) {
+ ___setErrNo(ERRNO_CODES.ENOTTY);
+ return 0;
+ }
+ return 1;
},
lchown__deps: ['chown'],
lchown: function(path, owner, group) {
@@ -1916,30 +1917,21 @@ LibraryManager.library = {
if (!_ttyname.ret) _ttyname.ret = _malloc(256);
return _ttyname_r(fildes, _ttyname.ret, 256) ? 0 : _ttyname.ret;
},
- ttyname_r__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'],
+ ttyname_r__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'isatty'],
ttyname_r: function(fildes, name, namesize) {
// int ttyname_r(int fildes, char *name, size_t namesize);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/ttyname.html
var stream = FS.streams[fildes];
+ var ttyname = '/dev/tty';
if (!stream) {
return ___setErrNo(ERRNO_CODES.EBADF);
- } else {
- var object = stream.object;
- if (!object.isDevice || !object.input || !object.output) {
- return ___setErrNo(ERRNO_CODES.ENOTTY);
- } else {
- var ret = stream.path;
- if (namesize < ret.length + 1) {
- return ___setErrNo(ERRNO_CODES.ERANGE);
- } else {
- for (var i = 0; i < ret.length; i++) {
- {{{ makeSetValue('name', 'i', 'ret.charCodeAt(i)', 'i8') }}}
- }
- {{{ makeSetValue('name', 'i', '0', 'i8') }}}
- return 0;
- }
- }
+ } else if (!_isatty(fildes)) {
+ return ___setErrNo(ERRNO_CODES.ENOTTY);
+ } else if (namesize < ttyname.length + 1) {
+ return ___setErrNo(ERRNO_CODES.ERANGE);
}
+ writeStringToMemory(ttyname, name);
+ return 0;
},
symlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'],
symlink: function(path1, path2) {
@@ -6223,10 +6215,282 @@ LibraryManager.library = {
},
strftime_l: 'strftime', // no locale support yet
+ strptime__deps: ['__tm_struct_layout'],
strptime: function(buf, format, tm) {
// char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tm);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html
- // TODO: Implement.
+ var pattern = Pointer_stringify(format);
+
+ // escape special characters
+ // TODO: not sure we really need to escape all of these in JS regexps
+ var SPECIAL_CHARS = '\\!@#$^&*()+=-[]/{}|:<>?,.';
+ for (var i=0, ii=SPECIAL_CHARS.length; i<ii; ++i) {
+ pattern = pattern.replace(new RegExp('\\'+SPECIAL_CHARS[i], 'g'), '\\'+SPECIAL_CHARS[i]);
+ }
+
+ // reduce number of matchers
+ var EQUIVALENT_MATCHERS = {
+ '%A': '%a',
+ '%B': '%b',
+ '%c': '%x\\s+%X',
+ '%D': '%m\\/%d\\/%y',
+ '%e': '%d',
+ '%h': '%b',
+ '%R': '%H\\:%M',
+ '%r': '%I\\:%M\\:%S\\s%p',
+ '%T': '%H\\:%M\\:%S',
+ '%x': '%m\\/%d\\/(?:%y|%Y)',
+ '%X': '%H\\:%M\\:%S'
+ };
+ for (var matcher in EQUIVALENT_MATCHERS) {
+ pattern = pattern.replace(matcher, EQUIVALENT_MATCHERS[matcher]);
+ }
+
+ // TODO: take care of locale
+
+ var DATE_PATTERNS = {
+ /* weeday name */ '%a': '(?:Sun(?:day)?)|(?:Mon(?:day)?)|(?:Tue(?:sday)?)|(?:Wed(?:nesday)?)|(?:Thu(?:rsday)?)|(?:Fri(?:day)?)|(?:Sat(?:urday)?)',
+ /* month name */ '%b': '(?:Jan(?:uary)?)|(?:Feb(?:ruary)?)|(?:Mar(?:ch)?)|(?:Apr(?:il)?)|May|(?:Jun(?:e)?)|(?:Jul(?:y)?)|(?:Aug(?:ust)?)|(?:Sep(?:tember)?)|(?:Oct(?:ober)?)|(?:Nov(?:ember)?)|(?:Dec(?:ember)?)',
+ /* century */ '%C': '\\d\\d',
+ /* day of month */ '%d': '0[1-9]|[1-9](?!\\d)|1\\d|2\\d|30|31',
+ /* hour (24hr) */ '%H': '\\d(?!\\d)|[0,1]\\d|20|21|22|23',
+ /* hour (12hr) */ '%I': '\\d(?!\\d)|0\\d|10|11|12',
+ /* day of year */ '%j': '00[1-9]|0?[1-9](?!\\d)|0?[1-9]\\d(?!\\d)|[1,2]\\d\\d|3[0-6]\\d',
+ /* month */ '%m': '0[1-9]|[1-9](?!\\d)|10|11|12',
+ /* minutes */ '%M': '0\\d|\\d(?!\\d)|[1-5]\\d',
+ /* whitespace */ '%n': '\\s',
+ /* AM/PM */ '%p': 'AM|am|PM|pm|A\\.M\\.|a\\.m\\.|P\\.M\\.|p\\.m\\.',
+ /* seconds */ '%S': '0\\d|\\d(?!\\d)|[1-5]\\d|60',
+ /* week number */ '%U': '0\\d|\\d(?!\\d)|[1-4]\\d|50|51|52|53',
+ /* week number */ '%W': '0\\d|\\d(?!\\d)|[1-4]\\d|50|51|52|53',
+ /* weekday number */ '%w': '[0-6]',
+ /* 2-digit year */ '%y': '\\d\\d',
+ /* 4-digit year */ '%Y': '\\d\\d\\d\\d',
+ /* % */ '%%': '%',
+ /* whitespace */ '%t': '\\s',
+ };
+
+ var MONTH_NUMBERS = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11};
+ var MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+ var MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+ var DAY_NUMBERS_SUN_FIRST = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};
+ var DAY_NUMBERS_MON_FIRST = {MON: 0, TUE: 1, WED: 2, THU: 3, FRI: 4, SAT: 5, SUN: 6};
+
+ var isLeapYear = function(year) {
+ return year%4===0 && (year%100!==0 || year%400===0);
+ };
+
+ var arraySum = function(array, index) {
+ var sum = 0;
+ for (var i=0; i<=index; sum += array[i++]);
+ return sum;
+ };
+
+ var addDays = function(date, days) {
+ while(days>0) {
+ var leap = isLeapYear(date.getFullYear());
+ var currentMonth = date.getMonth();
+ var daysInCurrentMonth = (leap ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR)[currentMonth];
+
+ if (days>daysInCurrentMonth-date.getDate()) {
+ // we spill over to next month
+ days -= (daysInCurrentMonth-date.getDate()+1);
+ date.setDate(1);
+ if (currentMonth<11) {
+ date.setMonth(currentMonth+1)
+ } else {
+ date.setMonth(0);
+ date.setFullYear(date.getFullYear()+1);
+ }
+ } else {
+ // we stay in current month
+ date.setDate(date.getDate()+days);
+ return date;
+ }
+ }
+ return date;
+ };
+
+ for (var datePattern in DATE_PATTERNS) {
+ pattern = pattern.replace(datePattern, '('+datePattern+DATE_PATTERNS[datePattern]+')');
+ }
+
+ // take care of capturing groups
+ var capture = [];
+ for (var i=pattern.indexOf('%'); i>=0; i=pattern.indexOf('%')) {
+ capture.push(pattern[i+1]);
+ pattern = pattern.replace(new RegExp('\\%'+pattern[i+1], 'g'), '');
+ }
+
+ var matches = new RegExp('^'+pattern).exec(Pointer_stringify(buf))
+ // Module['print'](Pointer_stringify(buf)+ ' is matched by '+((new RegExp('^'+pattern)).source)+' into: '+JSON.stringify(matches));
+
+ var initDate = function() {
+ var fixup = function(value, min, max) {
+ return (typeof value !== 'number' || isNaN(value)) ? min : (value>=min ? (value<=max ? value: max): min);
+ };
+
+ return {
+ year: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_year', 'i32') }}} + 1900 , 1970, 9999),
+ month: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_mon', 'i32') }}}, 0, 11),
+ day: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_mday', 'i32') }}}, 1, 31),
+ hour: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_hour', 'i32') }}}, 0, 23),
+ min: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_min', 'i32') }}}, 0, 59),
+ sec: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_sec', 'i32') }}}, 0, 59)
+ };
+ };
+
+ if (matches) {
+ var date = initDate();
+ var value;
+
+ var getMatch = function(symbol) {
+ var pos = capture.indexOf(symbol);
+ // check if symbol appears in regexp
+ if (pos >= 0) {
+ // return matched value or null (falsy!) for non-matches
+ return matches[pos+1];
+ }
+ return;
+ }
+
+ // seconds
+ if ((value=getMatch('S'))) {
+ date.sec = parseInt(value);
+ }
+
+ // minutes
+ if ((value=getMatch('M'))) {
+ date.min = parseInt(value);
+ }
+
+ // hours
+ if ((value=getMatch('H'))) {
+ // 24h clock
+ date.hour = parseInt(value);
+ } else if ((value = getMatch('I'))) {
+ // AM/PM clock
+ var hour = parseInt(value);
+ if ((value=getMatch('p'))) {
+ hour += value.toUpperCase()[0] === 'P' ? 12 : 0;
+ }
+ date.hour = hour;
+ }
+
+ // year
+ if ((value=getMatch('Y'))) {
+ // parse from four-digit year
+ date.year = parseInt(value);
+ } else if ((value=getMatch('y'))) {
+ // parse from two-digit year...
+ var year = parseInt(value);
+ if ((value=getMatch('C'))) {
+ // ...and century
+ year += parseInt(value)*100;
+ } else {
+ // ...and rule-of-thumb
+ year += year<69 ? 2000 : 1900;
+ }
+ date.year = year;
+ }
+
+ // month
+ if ((value=getMatch('m'))) {
+ // parse from month number
+ date.month = parseInt(value)-1;
+ } else if ((value=getMatch('b'))) {
+ // parse from month name
+ date.month = MONTH_NUMBERS[value.substring(0,3).toUpperCase()] || 0;
+ // TODO: derive month from day in year+year, week number+day of week+year
+ }
+
+ // day
+ if ((value=getMatch('d'))) {
+ // get day of month directly
+ date.day = parseInt(value);
+ } else if ((value=getMatch('j'))) {
+ // get day of month from day of year ...
+ var day = parseInt(value);
+ var leapYear = isLeapYear(date.year);
+ for (var month=0; month<12; ++month) {
+ var daysUntilMonth = arraySum(leapYear ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR, month-1);
+ if (day<=daysUntilMonth+(leapYear ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR)[month]) {
+ date.day = day-daysUntilMonth;
+ }
+ }
+ } else if ((value=getMatch('a'))) {
+ // get day of month from weekday ...
+ var weekDay = value.substring(0,3).toUpperCase();
+ if ((value=getMatch('U'))) {
+ // ... and week number (Sunday being first day of week)
+ // Week number of the year (Sunday as the first day of the week) as a decimal number [00,53].
+ // All days in a new year preceding the first Sunday are considered to be in week 0.
+ var weekDayNumber = DAY_NUMBERS_SUN_FIRST[weekDay];
+ var weekNumber = parseInt(value);
+
+ // January 1st
+ var janFirst = new Date(date.year, 0, 1);
+ var endDate;
+ if (janFirst.getDay() === 0) {
+ // Jan 1st is a Sunday, and, hence in the 1st CW
+ endDate = addDays(janFirst, weekDayNumber+7*(weekNumber-1));
+ } else {
+ // Jan 1st is not a Sunday, and, hence still in the 0th CW
+ endDate = addDays(janFirst, 7-janFirst.getDay()+weekDayNumber+7*(weekNumber-1));
+ }
+ date.day = endDate.getDate();
+ date.month = endDate.getMonth();
+ } else if ((value=getMatch('W'))) {
+ // ... and week number (Monday being first day of week)
+ // Week number of the year (Monday as the first day of the week) as a decimal number [00,53].
+ // All days in a new year preceding the first Monday are considered to be in week 0.
+ var weekDayNumber = DAY_NUMBERS_MON_FIRST[weekDay];
+ var weekNumber = parseInt(value);
+
+ // January 1st
+ var janFirst = new Date(date.year, 0, 1);
+ var endDate;
+ if (janFirst.getDay()===1) {
+ // Jan 1st is a Monday, and, hence in the 1st CW
+ endDate = addDays(janFirst, weekDayNumber+7*(weekNumber-1));
+ } else {
+ // Jan 1st is not a Monday, and, hence still in the 0th CW
+ endDate = addDays(janFirst, 7-janFirst.getDay()+1+weekDayNumber+7*(weekNumber-1));
+ }
+
+ date.day = endDate.getDate();
+ date.month = endDate.getMonth();
+ }
+ }
+
+ /*
+ tm_sec int seconds after the minute 0-61*
+ tm_min int minutes after the hour 0-59
+ tm_hour int hours since midnight 0-23
+ tm_mday int day of the month 1-31
+ tm_mon int months since January 0-11
+ tm_year int years since 1900
+ tm_wday int days since Sunday 0-6
+ tm_yday int days since January 1 0-365
+ tm_isdst int Daylight Saving Time flag
+ */
+
+ var fullDate = new Date(date.year, date.month, date.day, date.hour, date.min, date.sec, 0);
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_sec', 'fullDate.getSeconds()', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_min', 'fullDate.getMinutes()', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_hour', 'fullDate.getHours()', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_mday', 'fullDate.getDate()', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_mon', 'fullDate.getMonth()', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_year', 'fullDate.getFullYear()-1900', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_wday', 'fullDate.getDay()', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_yday', 'arraySum(isLeapYear(fullDate.getFullYear()) ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_isdst', '0', 'i32') }}}
+
+ // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F
+ // TODO: not sure that intArrayFromString handles all unicode characters correctly
+ return buf+intArrayFromString(matches[0]).length-1;
+ }
+
return 0;
},
strptime_l: 'strptime', // no locale support yet
diff --git a/src/library_browser.js b/src/library_browser.js
index d9fd3ee5..7f79b2bd 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -84,13 +84,17 @@ mergeInto(LibraryManager.library, {
var imagePlugin = {};
imagePlugin['canHandle'] = function(name) {
- return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/.exec(name);
+ return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/i.test(name);
};
imagePlugin['handle'] = function(byteArray, name, onload, onerror) {
var b = null;
if (Browser.hasBlobConstructor) {
try {
b = new Blob([byteArray], { type: getMimetype(name) });
+ if (b.size !== byteArray.length) { // Safari bug #118630
+ // Safari's Blob can only take an ArrayBuffer
+ b = new Blob([(new Uint8Array(byteArray)).buffer], { type: getMimetype(name) });
+ }
} catch(e) {
Runtime.warnOnce('Blob constructor present but fails: ' + e + '; falling back to blob builder');
}
diff --git a/src/library_gl.js b/src/library_gl.js
index e59492cf..959773bc 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -122,14 +122,6 @@ var LibraryGL = {
}
},
- // Linear lookup in one of the tables (buffers, programs, etc.). TODO: consider using a weakmap to make this faster, if it matters
- scan: function(table, object) {
- for (var item in table) {
- if (table[item] == object) return item;
- }
- return 0;
- },
-
// Find a token in a shader source string
findToken: function(source, token) {
function isIdentChar(ch) {
@@ -402,15 +394,15 @@ var LibraryGL = {
{{{ makeSetValue('p', 'i*4', 'result[i]', 'i32') }}};
}
} else if (result instanceof WebGLBuffer) {
- {{{ makeSetValue('p', '0', 'GL.scan(GL.buffers, result)', 'i32') }}};
+ {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}};
} else if (result instanceof WebGLProgram) {
- {{{ makeSetValue('p', '0', 'GL.scan(GL.programs, result)', 'i32') }}};
+ {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}};
} else if (result instanceof WebGLFramebuffer) {
- {{{ makeSetValue('p', '0', 'GL.scan(GL.framebuffers, result)', 'i32') }}};
+ {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}};
} else if (result instanceof WebGLRenderbuffer) {
- {{{ makeSetValue('p', '0', 'GL.scan(GL.renderbuffers, result)', 'i32') }}};
+ {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}};
} else if (result instanceof WebGLTexture) {
- {{{ makeSetValue('p', '0', 'GL.scan(GL.textures, result)', 'i32') }}};
+ {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}};
} else {
throw 'Unknown object returned from WebGL getParameter';
}
@@ -445,15 +437,15 @@ var LibraryGL = {
{{{ makeSetValue('p', 'i*4', 'result[i]', 'float') }}};
}
} else if (result instanceof WebGLBuffer) {
- {{{ makeSetValue('p', '0', 'GL.scan(GL.buffers, result)', 'float') }}};
+ {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}};
} else if (result instanceof WebGLProgram) {
- {{{ makeSetValue('p', '0', 'GL.scan(GL.programs, result)', 'float') }}};
+ {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}};
} else if (result instanceof WebGLFramebuffer) {
- {{{ makeSetValue('p', '0', 'GL.scan(GL.framebuffers, result)', 'float') }}};
+ {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}};
} else if (result instanceof WebGLRenderbuffer) {
- {{{ makeSetValue('p', '0', 'GL.scan(GL.renderbuffers, result)', 'float') }}};
+ {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}};
} else if (result instanceof WebGLTexture) {
- {{{ makeSetValue('p', '0', 'GL.scan(GL.textures, result)', 'float') }}};
+ {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}};
} else {
throw 'Unknown object returned from WebGL getParameter';
}
@@ -508,7 +500,9 @@ var LibraryGL = {
glGenTextures: function(n, textures) {
for (var i = 0; i < n; i++) {
var id = GL.getNewId(GL.textures);
- GL.textures[id] = Module.ctx.createTexture();
+ var texture = Module.ctx.createTexture();
+ texture.name = id;
+ GL.textures[id] = texture;
{{{ makeSetValue('textures', 'i*4', 'id', 'i32') }}};
}
},
@@ -517,7 +511,9 @@ var LibraryGL = {
glDeleteTextures: function(n, textures) {
for (var i = 0; i < n; i++) {
var id = {{{ makeGetValue('textures', 'i*4', 'i32') }}};
- Module.ctx.deleteTexture(GL.textures[id]);
+ var texture = GL.textures[id];
+ Module.ctx.deleteTexture(texture);
+ texture.name = 0;
GL.textures[id] = null;
}
},
@@ -622,7 +618,9 @@ var LibraryGL = {
glGenBuffers: function(n, buffers) {
for (var i = 0; i < n; i++) {
var id = GL.getNewId(GL.buffers);
- GL.buffers[id] = Module.ctx.createBuffer();
+ var buffer = Module.ctx.createBuffer();
+ buffer.name = id;
+ GL.buffers[id] = buffer;
{{{ makeSetValue('buffers', 'i*4', 'id', 'i32') }}};
}
},
@@ -631,7 +629,9 @@ var LibraryGL = {
glDeleteBuffers: function(n, buffers) {
for (var i = 0; i < n; i++) {
var id = {{{ makeGetValue('buffers', 'i*4', 'i32') }}};
- Module.ctx.deleteBuffer(GL.buffers[id]);
+ var buffer = GL.buffers[id];
+ Module.ctx.deleteBuffer(buffer);
+ buffer.name = 0;
GL.buffers[id] = null;
if (id == GL.currArrayBuffer) GL.currArrayBuffer = 0;
@@ -665,7 +665,9 @@ var LibraryGL = {
glGenRenderbuffers: function(n, renderbuffers) {
for (var i = 0; i < n; i++) {
var id = GL.getNewId(GL.renderbuffers);
- GL.renderbuffers[id] = Module.ctx.createRenderbuffer();
+ var renderbuffer = Module.ctx.createRenderbuffer();
+ renderbuffer.name = id;
+ GL.renderbuffers[id] = renderbuffer;
{{{ makeSetValue('renderbuffers', 'i*4', 'id', 'i32') }}};
}
},
@@ -674,8 +676,10 @@ var LibraryGL = {
glDeleteRenderbuffers: function(n, renderbuffers) {
for (var i = 0; i < n; i++) {
var id = {{{ makeGetValue('renderbuffers', 'i*4', 'i32') }}};
- Module.ctx.deleteRenderbuffer(GL.renderbuffers[id]);
- GL.renderbuffers[id];
+ var renderbuffer = GL.renderbuffers[id];
+ Module.ctx.deleteRenderbuffer(renderbuffer);
+ renderbuffer.name = 0;
+ GL.renderbuffers[id] = null;
}
},
@@ -1139,13 +1143,17 @@ var LibraryGL = {
glCreateProgram__sig: 'i',
glCreateProgram: function() {
var id = GL.getNewId(GL.programs);
- GL.programs[id] = Module.ctx.createProgram();
+ var program = Module.ctx.createProgram();
+ program.name = id;
+ GL.programs[id] = program;
return id;
},
glDeleteProgram__sig: 'vi',
glDeleteProgram: function(program) {
- Module.ctx.deleteProgram(GL.programs[program]);
+ var program = GL.programs[program];
+ Module.ctx.deleteProgram(program);
+ program.name = 0;
GL.programs[program] = null;
GL.uniformTable[program] = null;
},
@@ -1221,7 +1229,9 @@ var LibraryGL = {
glGenFramebuffers: function(n, ids) {
for (var i = 0; i < n; ++i) {
var id = GL.getNewId(GL.framebuffers);
- GL.framebuffers[id] = Module.ctx.createFramebuffer();
+ var framebuffer = Module.ctx.createFramebuffer();
+ framebuffer.name = id;
+ GL.framebuffers[id] = framebuffer;
{{{ makeSetValue('ids', 'i*4', 'id', 'i32') }}};
}
},
@@ -1230,7 +1240,9 @@ var LibraryGL = {
glDeleteFramebuffers: function(n, framebuffers) {
for (var i = 0; i < n; ++i) {
var id = {{{ makeGetValue('framebuffers', 'i*4', 'i32') }}};
- Module.ctx.deleteFramebuffer(GL.framebuffers[id]);
+ var framebuffer = GL.framebuffers[id];
+ Module.ctx.deleteFramebuffer(framebuffer);
+ framebuffer.name = 0;
GL.framebuffers[id] = null;
}
},
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 176a2fae..b7d73862 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -21,6 +21,8 @@ var LibrarySDL = {
version: null,
surfaces: {},
+ // A pool of freed canvas elements. Reusing them avoids GC pauses.
+ canvasPool: [],
events: [],
fonts: [null],
@@ -246,7 +248,7 @@ var LibrarySDL = {
},
translateRGBAToCSSRGBA: function(r, g, b, a) {
- return 'rgba(' + r + ',' + g + ',' + b + ',' + (a/255) + ')';
+ return 'rgba(' + (r&0xff) + ',' + (g&0xff) + ',' + (b&0xff) + ',' + (a&0xff)/255 + ')';
},
translateRGBAToColor: function(r, g, b, a) {
@@ -288,7 +290,11 @@ var LibrarySDL = {
SDL.GL = SDL.GL || useWebGL;
var canvas;
if (!usePageCanvas) {
- canvas = document.createElement('canvas');
+ if (SDL.canvasPool.length > 0) {
+ canvas = SDL.canvasPool.pop();
+ } else {
+ canvas = document.createElement('canvas');
+ }
canvas.width = width;
canvas.height = height;
} else {
@@ -355,14 +361,56 @@ var LibrarySDL = {
},
freeSurface: function(surf) {
- _free(SDL.surfaces[surf].buffer);
- _free(SDL.surfaces[surf].pixelFormat);
+ var info = SDL.surfaces[surf];
+ if (!info.usePageCanvas && info.canvas) SDL.canvasPool.push(info.canvas);
+ _free(info.buffer);
+ _free(info.pixelFormat);
_free(surf);
SDL.surfaces[surf] = null;
},
+ touchX:0, touchY: 0,
+
receiveEvent: function(event) {
switch(event.type) {
+ case 'touchstart':
+ event.preventDefault();
+ var touch = event.touches[0];
+ touchX = touch.pageX;
+ touchY = touch.pageY;
+ var event = {
+ type: 'mousedown',
+ button: 0,
+ pageX: touchX,
+ pageY: touchY
+ };
+ SDL.DOMButtons[0] = 1;
+ SDL.events.push(event);
+ break;
+ case 'touchmove':
+ event.preventDefault();
+ var touch = event.touches[0];
+ touchX = touch.pageX;
+ touchY = touch.pageY;
+ event = {
+ type: 'mousemove',
+ button: 0,
+ pageX: touchX,
+ pageY: touchY
+ };
+ SDL.events.push(event);
+ break;
+ case 'touchend':
+ event.preventDefault();
+ event = {
+ type: 'mouseup',
+ button: 0,
+ pageX: touchX,
+ pageY: touchY
+ };
+ SDL.DOMButtons[0] = 0;
+ SDL.events.push(event);
+ break;
case 'mousemove':
if (Browser.pointerLock) {
// workaround for firefox bug 750111
@@ -1022,6 +1070,7 @@ var LibrarySDL = {
surfData.ctx.fillStyle = SDL.translateColorToCSSRGBA(color);
surfData.ctx.fillRect(r.x, r.y, r.w, r.h);
surfData.ctx.restore();
+ return 0;
},
SDL_BlitSurface__deps: ['SDL_UpperBlit'],
@@ -1031,11 +1080,19 @@ var LibrarySDL = {
zoomSurface: function(src, x, y, smooth) {
var srcData = SDL.surfaces[src];
- var w = srcData.width*x;
- var h = srcData.height*y;
- var ret = SDL.makeSurface(w, h, srcData.flags, false, 'zoomSurface');
+ var w = srcData.width * x;
+ var h = srcData.height * y;
+ var ret = SDL.makeSurface(Math.abs(w), Math.abs(h), srcData.flags, false, 'zoomSurface');
var dstData = SDL.surfaces[ret];
- dstData.ctx.drawImage(srcData.canvas, 0, 0, w, h);
+ if (x >= 0 && y >= 0) dstData.ctx.drawImage(srcData.canvas, 0, 0, w, h);
+ else {
+ dstData.ctx.save();
+ dstData.ctx.scale(x < 0 ? -1 : 1, y < 0 ? -1 : 1);
+ dstData.ctx.drawImage(srcData.canvas, w < 0 ? w : 0, h < 0 ? h : 0, Math.abs(w), Math.abs(h));
+ // XXX I think this should work according to the spec, but currently
+ // fails on FF: dstData.ctx.drawImage(srcData.canvas, 0, 0, w, h);
+ dstData.ctx.restore();
+ }
return ret;
},
@@ -1056,6 +1113,14 @@ var LibrarySDL = {
SDL.surfaces[surf].alpha = alpha;
},
+ SDL_SetColorKey: function(surf, flag, key) {
+ // SetColorKey assigns one color to be rendered as transparent. I don't
+ // think the canvas API allows for anything like this, and iterating through
+ // each pixel to replace that color seems prohibitively expensive.
+ Runtime.warnOnce('SDL_SetColorKey is a no-op for performance reasons');
+ return 0;
+ },
+
SDL_GetTicks: function() {
return Math.floor(Date.now() - SDL.startTime);
},
@@ -1472,15 +1537,20 @@ var LibrarySDL = {
},
Mix_HaltChannel: function(channel) {
- var info = SDL.channels[channel];
- if (info.audio) {
- info.audio.pause();
- info.audio = null;
- } else {
- Module.printErr('No Audio for channel: ' + channel);
+ function halt(channel) {
+ var info = SDL.channels[channel];
+ if (info.audio) {
+ info.audio.pause();
+ info.audio = null;
+ }
+ if (SDL.channelFinished) {
+ Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
+ }
}
- if (SDL.channelFinished) {
- Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
+ if (channel != -1) {
+ halt(channel);
+ } else {
+ for (var i = 0; i < SDL.channels.length; ++i) halt(i);
}
return 0;
},
@@ -1667,6 +1737,7 @@ var LibrarySDL = {
},
TTF_RenderText_Blended: 'TTF_RenderText_Solid', // XXX ignore blending vs. solid
TTF_RenderText_Shaded: 'TTF_RenderText_Solid', // XXX ignore blending vs. solid
+ TTF_RenderUTF8_Solid: 'TTF_RenderText_Solid',
TTF_SizeText: function(font, text, w, h) {
var fontData = SDL.fonts[font];
@@ -1691,35 +1762,114 @@ var LibrarySDL = {
// SDL gfx
+ $SDL_gfx: {
+ drawRectangle: function(surf, x1, y1, x2, y2, action, cssColor) {
+ x1 = x1 << 16 >> 16;
+ y1 = y1 << 16 >> 16;
+ x2 = x2 << 16 >> 16;
+ y2 = y2 << 16 >> 16;
+ var surfData = SDL.surfaces[surf];
+ assert(!surfData.locked); // but we could unlock and re-lock if we must..
+ // TODO: if ctx does not change, leave as is, and also do not re-set xStyle etc.
+ var x = x1 < x2 ? x1 : x2;
+ var y = y1 < y2 ? y1 : y2;
+ var w = Math.abs(x2 - x1);
+ var h = Math.abs(y2 - y1);
+ surfData.ctx.save();
+ surfData.ctx[action + 'Style'] = cssColor;
+ surfData.ctx[action + 'Rect'](x, y, w, h);
+ surfData.ctx.restore();
+ },
+ drawLine: function(surf, x1, y1, x2, y2, cssColor) {
+ x1 = x1 << 16 >> 16;
+ y1 = y1 << 16 >> 16;
+ x2 = x2 << 16 >> 16;
+ y2 = y2 << 16 >> 16;
+ var surfData = SDL.surfaces[surf];
+ assert(!surfData.locked); // but we could unlock and re-lock if we must..
+ surfData.ctx.save();
+ surfData.ctx.strokeStyle = cssColor;
+ surfData.ctx.beginPath();
+ surfData.ctx.moveTo(x1, y1);
+ surfData.ctx.lineTo(x2, y2);
+ surfData.ctx.stroke();
+ surfData.ctx.restore();
+ },
+ // See http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
+ drawEllipse: function(surf, x, y, rx, ry, action, cssColor) {
+ x = x << 16 >> 16;
+ y = y << 16 >> 16;
+ rx = rx << 16 >> 16;
+ ry = ry << 16 >> 16;
+ var surfData = SDL.surfaces[surf];
+ assert(!surfData.locked); // but we could unlock and re-lock if we must..
+
+ surfData.ctx.save();
+ surfData.ctx.beginPath();
+ surfData.ctx.translate(x, y);
+ surfData.ctx.scale(rx, ry);
+ surfData.ctx.arc(0, 0, 1, 0, 2 * Math.PI);
+ surfData.ctx.restore();
+
+ surfData.ctx.save();
+ surfData.ctx[action + 'Style'] = cssColor;
+ surfData.ctx[action]();
+ surfData.ctx.restore();
+ },
+ // the gfx library uses something different from the rest of SDL...
+ translateColorToCSSRGBA: function(rgba) {
+ return 'rgba(' + (rgba>>>24) + ',' + (rgba>>16 & 0xff) + ',' + (rgba>>8 & 0xff) + ',' + (rgba&0xff) + ')';
+ }
+ },
+
+ boxColor__deps: ['$SDL_gfx'],
+ boxColor: function(surf, x1, y1, x2, y2, color) {
+ return SDL_gfx.drawRectangle(surf, x1, y1, x2, y2, 'fill', SDL_gfx.translateColorToCSSRGBA(color));
+ },
+
+ boxRGBA__deps: ['$SDL_gfx'],
boxRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
- var surfData = SDL.surfaces[surf];
- assert(!surfData.locked); // but we could unlock and re-lock if we must..
- // TODO: if ctx does not change, leave as is, and also do not re-set xStyle etc.
- surfData.ctx.save();
- surfData.ctx.fillStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
- surfData.ctx.fillRect(x1, y1, x2-x1, y2-y1);
- surfData.ctx.restore();
+ return SDL_gfx.drawRectangle(surf, x1, y1, x2, y2, 'fill', SDL.translateRGBAToCSSRGBA(r, g, b, a));
+ },
+
+ rectangleColor__deps: ['$SDL_gfx'],
+ rectangleColor: function(surf, x1, y1, x2, y2, color) {
+ return SDL_gfx.drawRectangle(surf, x1, y1, x2, y2, 'stroke', SDL_gfx.translateColorToCSSRGBA(color));
},
+ rectangleRGBA__deps: ['$SDL_gfx'],
rectangleRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
- var surfData = SDL.surfaces[surf];
- assert(!surfData.locked); // but we could unlock and re-lock if we must..
- surfData.ctx.save();
- surfData.ctx.strokeStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
- surfData.ctx.strokeRect(x1, y1, x2-x1, y2-y1);
- surfData.ctx.restore();
+ return SDL_gfx.drawRectangle(surf, x1, y1, x2, y2, 'stroke', SDL.translateRGBAToCSSRGBA(r, g, b, a));
+ },
+
+ ellipseColor__deps: ['$SDL_gfx'],
+ ellipseColor: function(surf, x, y, rx, ry, color) {
+ return SDL_gfx.drawEllipse(surf, x, y, rx, ry, 'stroke', SDL_gfx.translateColorToCSSRGBA(color));
+ },
+
+ ellipseRGBA__deps: ['$SDL_gfx'],
+ ellipseRGBA: function(surf, x, y, rx, ry, r, g, b, a) {
+ return SDL_gfx.drawEllipse(surf, x, y, rx, ry, 'stroke', SDL.translateRGBAToCSSRGBA(r, g, b, a));
+ },
+
+ filledEllipseColor__deps: ['$SDL_gfx'],
+ filledEllipseColor: function(surf, x, y, rx, ry, color) {
+ return SDL_gfx.drawEllipse(surf, x, y, rx, ry, 'fill', SDL_gfx.translateColorToCSSRGBA(color));
},
+ filledEllipseRGBA__deps: ['$SDL_gfx'],
+ filledEllipseRGBA: function(surf, x, y, rx, ry, r, g, b, a) {
+ return SDL_gfx.drawEllipse(surf, x, y, rx, ry, 'fill', SDL.translateRGBAToCSSRGBA(r, g, b, a));
+ },
+
+ lineColor__deps: ['$SDL_gfx'],
+ lineColor: function(surf, x1, y1, x2, y2, color) {
+ return SDL_gfx.drawLine(surf, x1, y1, x2, y2, SDL_gfx.translateColorToCSSRGBA(color));
+ },
+
+ lineRGBA__deps: ['$SDL_gfx'],
lineRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
- var surfData = SDL.surfaces[surf];
- assert(!surfData.locked); // but we could unlock and re-lock if we must..
- surfData.ctx.save();
- surfData.ctx.strokeStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
- surfData.ctx.beginPath();
- surfData.ctx.moveTo(x1, y1);
- surfData.ctx.lineTo(x2, y2);
- surfData.ctx.stroke();
- surfData.ctx.restore();
+ return SDL_gfx.drawLine(surf, x1, y1, x2, y2, SDL.translateRGBAToCSSRGBA(r, g, b, a));
},
pixelRGBA__deps: ['boxRGBA'],
@@ -1805,12 +1955,18 @@ var LibrarySDL = {
return -1;
},
+ // Joysticks
+
+ SDL_NumJoysticks: function() { return 0 },
+
+ SDL_JoystickOpen: function(deviceIndex) { return 0 },
+
+ SDL_JoystickGetButton: function(joystick, button) { return 0 },
+
// Misc
SDL_InitSubSystem: function(flags) { return 0 },
- SDL_NumJoysticks: function() { return 0 },
-
SDL_RWFromFile: function(filename, mode) {
return filename; // XXX We just forward the filename
},
diff --git a/src/parseTools.js b/src/parseTools.js
index dfd4b7ed..eb200c65 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -2222,9 +2222,9 @@ function processMathop(item) {
// basic integer ops
case 'add': return handleOverflow(getFastValue(idents[0], '+', idents[1], item.type), bits);
case 'sub': return handleOverflow(getFastValue(idents[0], '-', idents[1], item.type), bits);
- case 'sdiv': case 'udiv': return makeRounding(getFastValue(idents[0], '/', idents[1], item.type), bits, op[0] === 's');
+ case 'sdiv': case 'udiv': return makeRounding(getFastValue(idents[0], '/', idents[1], item.type), bits, true);
case 'mul': return getFastValue(idents[0], '*', idents[1], item.type); // overflow handling is already done in getFastValue for '*'
- case 'urem': case 'srem': return makeRounding(getFastValue(idents[0], '%', idents[1], item.type), bits, op[0] === 's');
+ case 'urem': case 'srem': return makeRounding(getFastValue(idents[0], '%', idents[1], item.type), bits, true);
case 'or': {
if (bits > 32) {
assert(bits === 64, 'Too many bits for or: ' + bits);
diff --git a/system/include/SDL/SDL_keycode.h b/system/include/SDL/SDL_keycode.h
index 472ca28e..84063b8b 100644
--- a/system/include/SDL/SDL_keycode.h
+++ b/system/include/SDL/SDL_keycode.h
@@ -310,7 +310,8 @@ enum
SDLK_KBDILLUMDOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KBDILLUMDOWN),
SDLK_KBDILLUMUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KBDILLUMUP),
SDLK_EJECT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_EJECT),
- SDLK_SLEEP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SLEEP)
+ SDLK_SLEEP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SLEEP),
+ SDLK_LAST = SDL_SCANCODE_TO_KEYCODE(SDL_NUM_SCANCODES)
};
/**
diff --git a/system/include/libc/sys/dirent.h b/system/include/libc/sys/dirent.h
index 0d8b02b5..e6ce831e 100644
--- a/system/include/libc/sys/dirent.h
+++ b/system/include/libc/sys/dirent.h
@@ -24,6 +24,7 @@ DIR *opendir(const char *);
void seekdir(DIR *, long);
long telldir(DIR *);
DIR *readdir(DIR *);
+int readdir_r(DIR *, struct dirent *, struct dirent **);
int closedir(DIR *dirp);
void rewinddir(DIR *dirp);
int scandir(const char *dirp,
diff --git a/tests/cases/zeroextarg.ll b/tests/cases/zeroextarg.ll
new file mode 100644
index 00000000..25efb7ec
--- /dev/null
+++ b/tests/cases/zeroextarg.ll
@@ -0,0 +1,22 @@
+; ModuleID = 'tests/hello_world.bc'
+target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"
+target triple = "i386-pc-linux-gnu"
+
+@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*]
+
+define void @glSampleCoverage(float %18, i8 zeroext %invert) {
+entry:
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32]
+ ret void
+}
+
+; [#uses=0]
+define i32 @main() {
+entry:
+ tail call void @glSampleCoverage(float 3.5, i8 zeroext 12)
+ ret i32 1
+}
+
+; [#uses=1]
+declare i32 @printf(i8*, ...)
+
diff --git a/tests/dirent/test_readdir.c b/tests/dirent/test_readdir.c
new file mode 100644
index 00000000..9f7b12e8
--- /dev/null
+++ b/tests/dirent/test_readdir.c
@@ -0,0 +1,132 @@
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+static void create_file(const char *path, const char *buffer, int mode) {
+ int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+ assert(fd >= 0);
+
+ int err = write(fd, buffer, sizeof(char) * strlen(buffer));
+ assert(err == (sizeof(char) * strlen(buffer)));
+
+ close(fd);
+}
+
+void setup() {
+ mkdir("nocanread", 0111);
+ mkdir("foobar", 0777);
+ create_file("foobar/file.txt", "ride into the danger zone", 0666);
+}
+
+void cleanup() {
+ rmdir("nocanread");
+ unlink("foobar/file.txt");
+ rmdir("foobar");
+}
+
+void test() {
+ int err;
+ int loc;
+ DIR *dir;
+ struct dirent *ent;
+ struct dirent ent_r;
+ struct dirent *result;
+
+ // check bad opendir input
+ dir = opendir("noexist");
+ assert(!dir);
+ assert(errno == ENOENT);
+ dir = opendir("nocanread");
+ assert(!dir);
+ assert(errno == EACCES);
+ dir = opendir("foobar/file.txt");
+ assert(!dir);
+ assert(errno == ENOTDIR);
+
+ // check bad readdir input
+ dir = opendir("foobar");
+ closedir(dir);
+ ent = readdir(dir);
+ assert(!ent);
+ assert(errno == EBADF);
+
+ // check bad readdir_r input
+ dir = opendir("foobar");
+ closedir(dir);
+ err = readdir_r(dir, NULL, &result);
+ assert(err == EBADF);
+
+ //
+ // do a normal read with readdir
+ //
+ dir = opendir("foobar");
+ assert(dir);
+ ent = readdir(dir);
+ assert(!strcmp(ent->d_name, "."));
+ assert(ent->d_type & DT_DIR);
+ ent = readdir(dir);
+ assert(!strcmp(ent->d_name, ".."));
+ assert(ent->d_type & DT_DIR);
+ ent = readdir(dir);
+ assert(!strcmp(ent->d_name, "file.txt"));
+ assert(ent->d_type & DT_REG);
+ ent = readdir(dir);
+ assert(!ent);
+
+ // test rewinddir
+ rewinddir(dir);
+ ent = readdir(dir);
+ assert(!strcmp(ent->d_name, "."));
+
+ // test seek / tell
+ rewinddir(dir);
+ ent = readdir(dir);
+ assert(!strcmp(ent->d_name, "."));
+ loc = telldir(dir);
+ ent = readdir(dir);
+ assert(!strcmp(ent->d_name, ".."));
+ ent = readdir(dir);
+ assert(!strcmp(ent->d_name, "file.txt"));
+ seekdir(dir, loc);
+ ent = readdir(dir);
+ assert(!strcmp(ent->d_name, ".."));
+
+ //
+ // do a normal read with readdir_r
+ //
+ rewinddir(dir);
+ err = readdir_r(dir, &ent_r, &result);
+ assert(!err);
+ assert(&ent_r == result);
+ assert(!strcmp(ent_r.d_name, "."));
+ err = readdir_r(dir, &ent_r, &result);
+ assert(!err);
+ assert(&ent_r == result);
+ assert(!strcmp(ent_r.d_name, ".."));
+ err = readdir_r(dir, &ent_r, &result);
+ assert(!err);
+ assert(&ent_r == result);
+ assert(!strcmp(ent_r.d_name, "file.txt"));
+ err = readdir_r(dir, &ent_r, &result);
+ assert(!err);
+ assert(!result);
+
+ err = closedir(dir);
+ assert(!err);
+
+ puts("success");
+}
+
+int main() {
+ atexit(cleanup);
+ signal(SIGABRT, cleanup);
+ setup();
+ test();
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/tests/fcntl-misc/src.c b/tests/fcntl-misc/src.c
index 73734969..7cdbbcd6 100644
--- a/tests/fcntl-misc/src.c
+++ b/tests/fcntl-misc/src.c
@@ -6,7 +6,7 @@
int main() {
struct stat s;
- int f = open("/test", O_RDONLY, 0777);
+ int f = open("/test", O_RDWR, 0777);
printf("posix_fadvise: %d\n", posix_fadvise(f, 3, 2, POSIX_FADV_DONTNEED));
printf("errno: %d\n", errno);
diff --git a/tests/fcntl-open/output.txt b/tests/fcntl-open/output.txt
index 314ae880..07b106ac 100644
--- a/tests/fcntl-open/output.txt
+++ b/tests/fcntl-open/output.txt
@@ -19,8 +19,8 @@ errno: 0
st_mode: 0100000
EXISTING FOLDER 0,1
-success: 0
-errno: 21
+success: 1
+errno: 0
st_mode: 040000
NON-EXISTING 0,1
@@ -139,8 +139,8 @@ errno: 0
st_mode: 0100000
EXISTING FOLDER 0,9
-success: 0
-errno: 21
+success: 1
+errno: 0
st_mode: 040000
NON-EXISTING 0,9
@@ -720,4 +720,4 @@ st_mode: 0100000
CREAT
success: 1
-errno: 0
+errno: 0 \ No newline at end of file
diff --git a/tests/fcntl-open/src.c b/tests/fcntl-open/src.c
index 52d2e7e4..bd52dd3f 100644
--- a/tests/fcntl-open/src.c
+++ b/tests/fcntl-open/src.c
@@ -1,13 +1,45 @@
-#include <stdio.h>
+#include <assert.h>
#include <errno.h>
-#include <sys/stat.h>
#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
-int main() {
+char nonexistent_name[] = "noexist-##";
+
+void create_file(const char *path, const char *buffer, int mode) {
+ int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+ assert(fd >= 0);
+
+ int err = write(fd, buffer, sizeof(char) * strlen(buffer));
+ assert(err == (sizeof(char) * strlen(buffer)));
+
+ close(fd);
+}
+
+void setup() {
+ create_file("test-file", "abcdef", 0777);
+ mkdir("test-folder", 0777);
+}
+
+void cleanup() {
+ unlink("test-file");
+ rmdir("test-folder");
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 16; j++) {
+ nonexistent_name[8] = 'a' + i;
+ nonexistent_name[9] = 'a' + j;
+ unlink(nonexistent_name);
+ }
+ }
+ unlink("creat-me");
+}
+
+void test() {
struct stat s;
int modes[] = {O_RDONLY, O_WRONLY, O_RDWR};
- char nonexistent_name[] = "/noexist-##";
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 16; j++) {
@@ -18,25 +50,25 @@ int main() {
if (j & 0x8) flags |= O_APPEND;
printf("EXISTING FILE %d,%d\n", i, j);
- printf("success: %d\n", open("/test-file", flags, 0777) != -1);
+ printf("success: %d\n", open("test-file", flags, 0777) != -1);
printf("errno: %d\n", errno);
- stat("/test-file", &s);
+ stat("test-file", &s);
printf("st_mode: 0%o\n", s.st_mode & 037777777000);
memset(&s, 0, sizeof s);
printf("\n");
errno = 0;
printf("EXISTING FOLDER %d,%d\n", i, j);
- printf("success: %d\n", open("/test-folder", flags, 0777) != -1);
+ printf("success: %d\n", open("test-folder", flags, 0777) != -1);
printf("errno: %d\n", errno);
- stat("/test-folder", &s);
+ stat("test-folder", &s);
printf("st_mode: 0%o\n", s.st_mode & 037777777000);
memset(&s, 0, sizeof s);
printf("\n");
errno = 0;
- nonexistent_name[9] = 'a' + i;
- nonexistent_name[10] = 'a' + j;
+ nonexistent_name[8] = 'a' + i;
+ nonexistent_name[9] = 'a' + j;
printf("NON-EXISTING %d,%d\n", i, j);
printf("success: %d\n", open(nonexistent_name, flags, 0777) != -1);
printf("errno: %d\n", errno);
@@ -49,8 +81,14 @@ int main() {
}
printf("CREAT\n");
- printf("success: %d\n", creat("/creat-me", 0777) != -1);
+ printf("success: %d\n", creat("creat-me", 0777) != -1);
printf("errno: %d\n", errno);
-
- return 0;
}
+
+int main() {
+ atexit(cleanup);
+ signal(SIGABRT, cleanup);
+ setup();
+ test();
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/tests/runner.py b/tests/runner.py
index 4bd4b806..1f6b39f4 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -4958,6 +4958,130 @@ The current type of b is: 9
'''
self.do_run(src, 'time: ') # compilation check, mainly
+
+ def test_strptime_tm(self):
+ src=r'''
+ #include <time.h>
+ #include <stdio.h>
+ #include <string.h>
+
+ int main() {
+ struct tm tm;
+ char *ptr = strptime("17410105012000", "%H%M%S%d%m%Y", &tm);
+
+ printf("%s: %s, %d/%d/%d %d:%d:%d",
+ (ptr != NULL && *ptr=='\0') ? "OK" : "ERR",
+ tm.tm_wday == 0 ? "Sun" : (tm.tm_wday == 1 ? "Mon" : (tm.tm_wday == 2 ? "Tue" : (tm.tm_wday == 3 ? "Wed" : (tm.tm_wday == 4 ? "Thu" : (tm.tm_wday == 5 ? "Fri" : (tm.tm_wday == 6 ? "Sat" : "ERR")))))),
+ tm.tm_mon+1,
+ tm.tm_mday,
+ tm.tm_year+1900,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec
+ );
+ }
+ '''
+ self.do_run(src, 'OK: Wed, 1/5/2000 17:41:1')
+
+ def test_strptime_days(self):
+ src = r'''
+ #include <time.h>
+ #include <stdio.h>
+ #include <string.h>
+
+ static const struct {
+ const char *input;
+ const char *format;
+ } day_tests[] = {
+ { "2000-01-01", "%Y-%m-%d"},
+ { "03/03/00", "%D"},
+ { "9/9/99", "%x"},
+ { "19990502123412", "%Y%m%d%H%M%S"},
+ { "2001 20 Mon", "%Y %U %a"},
+ { "2006 4 Fri", "%Y %U %a"},
+ { "2001 21 Mon", "%Y %W %a"},
+ { "2013 29 Wed", "%Y %W %a"},
+ { "2000-01-01 08:12:21 AM", "%Y-%m-%d %I:%M:%S %p"},
+ { "2000-01-01 08:12:21 PM", "%Y-%m-%d %I:%M:%S %p"},
+ { "2001 17 Tue", "%Y %U %a"},
+ { "2001 8 Thursday", "%Y %W %a"},
+ };
+
+ int main() {
+ struct tm tm;
+
+ for (int i = 0; i < sizeof (day_tests) / sizeof (day_tests[0]); ++i) {
+ memset (&tm, '\0', sizeof (tm));
+ char *ptr = strptime(day_tests[i].input, day_tests[i].format, &tm);
+
+ printf("%s: %d/%d/%d (%dth DoW, %dth DoY)\n", (ptr != NULL && *ptr=='\0') ? "OK" : "ERR", tm.tm_mon+1, tm.tm_mday, 1900+tm.tm_year, tm.tm_wday, tm.tm_yday);
+ }
+ }
+ '''
+ self.do_run(src, 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\
+ 'OK: 3/3/2000 (5th DoW, 62th DoY)\n'\
+ 'OK: 9/9/1999 (4th DoW, 251th DoY)\n'\
+ 'OK: 5/2/1999 (0th DoW, 121th DoY)\n'\
+ 'OK: 5/21/2001 (1th DoW, 140th DoY)\n'\
+ 'OK: 1/27/2006 (5th DoW, 26th DoY)\n'\
+ 'OK: 5/21/2001 (1th DoW, 140th DoY)\n'\
+ 'OK: 7/24/2013 (3th DoW, 204th DoY)\n'\
+ 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\
+ 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\
+ 'OK: 5/1/2001 (2th DoW, 120th DoY)\n'\
+ 'OK: 2/22/2001 (4th DoW, 52th DoY)\n'\
+ )
+
+ def test_strptime_reentrant(self):
+ src=r'''
+ #include <time.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+
+ int main () {
+ int result = 0;
+ struct tm tm;
+
+ memset (&tm, 0xaa, sizeof (tm));
+
+ /* Test we don't crash on uninitialized struct tm.
+ Some fields might contain bogus values until everything
+ needed is initialized, but we shouldn't crash. */
+ if (strptime ("2007", "%Y", &tm) == NULL
+ || strptime ("12", "%d", &tm) == NULL
+ || strptime ("Feb", "%b", &tm) == NULL
+ || strptime ("13", "%M", &tm) == NULL
+ || strptime ("21", "%S", &tm) == NULL
+ || strptime ("16", "%H", &tm) == NULL) {
+ printf("ERR: returned NULL");
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16
+ || tm.tm_mday != 12 || tm.tm_mon != 1 || tm.tm_year != 107
+ || tm.tm_wday != 1 || tm.tm_yday != 42) {
+ printf("ERR: unexpected tm content (1) - %d/%d/%d %d:%d:%d", tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ exit(EXIT_FAILURE);
+ }
+
+ if (strptime ("8", "%d", &tm) == NULL) {
+ printf("ERR: strptime failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16
+ || tm.tm_mday != 8 || tm.tm_mon != 1 || tm.tm_year != 107
+ || tm.tm_wday != 4 || tm.tm_yday != 38) {
+ printf("ERR: unexpected tm content (2) - %d/%d/%d %d:%d:%d", tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("OK");
+ }
+ '''
+ self.do_run(src, 'OK')
+
def test_intentional_fault(self):
# Some programs intentionally segfault themselves, we should compile that into a throw
src = r'''
@@ -6794,84 +6918,9 @@ def process(filename):
self.emcc_args += ['--embed-file', 'three_numbers.txt']
self.do_run(src, 'match = 3\nx = -1.0, y = 0.1, z = -0.1\n')
- def test_folders(self):
- add_pre_run = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- \'\'\'
- FS.createFolder('/', 'test', true, false);
- FS.createPath('/', 'test/hello/world/', true, false);
- FS.createPath('/test', 'goodbye/world/', true, false);
- FS.createPath('/test/goodbye', 'noentry', false, false);
- FS.createDataFile('/test', 'freeforall.ext', 'abc', true, true);
- FS.createDataFile('/test', 'restricted.ext', 'def', false, false);
- \'\'\'
- )
- open(filename, 'w').write(src)
-'''
- src = r'''
- #include <stdio.h>
- #include <dirent.h>
- #include <errno.h>
-
- int main() {
- struct dirent *e;
-
- // Basic correct behaviour.
- DIR* d = opendir("/test");
- printf("--E: %d\n", errno);
- while ((e = readdir(d))) puts(e->d_name);
- printf("--E: %d\n", errno);
-
- // Empty folder; tell/seek.
- puts("****");
- d = opendir("/test/hello/world/");
- e = readdir(d);
- puts(e->d_name);
- int pos = telldir(d);
- e = readdir(d);
- puts(e->d_name);
- seekdir(d, pos);
- e = readdir(d);
- puts(e->d_name);
-
- // Errors.
- puts("****");
- printf("--E: %d\n", errno);
- d = opendir("/test/goodbye/noentry");
- printf("--E: %d, D: %d\n", errno, d);
- d = opendir("/i/dont/exist");
- printf("--E: %d, D: %d\n", errno, d);
- d = opendir("/test/freeforall.ext");
- printf("--E: %d, D: %d\n", errno, d);
- while ((e = readdir(d))) puts(e->d_name);
- printf("--E: %d\n", errno);
-
- return 0;
- }
- '''
- expected = '''
- --E: 0
- .
- ..
- hello
- goodbye
- freeforall.ext
- restricted.ext
- --E: 0
- ****
- .
- ..
- ..
- ****
- --E: 0
- --E: 13, D: 0
- --E: 2, D: 0
- --E: 20, D: 0
- --E: 9
- '''
- self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected), post_build=add_pre_run)
+ def test_readdir(self):
+ src = open(path_from_root('tests', 'dirent', 'test_readdir.c'), 'r').read()
+ self.do_run(src, 'success', force_c=True)
def test_stat(self):
add_pre_run = '''
@@ -6906,20 +6955,9 @@ def process(filename):
self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
def test_fcntl_open(self):
- add_pre_run = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- \'\'\'
- FS.createDataFile('/', 'test-file', 'abcdef', true, true);
- FS.createFolder('/', 'test-folder', true, true);
- \'\'\'
- )
- open(filename, 'w').write(src)
-'''
src = open(path_from_root('tests', 'fcntl-open', 'src.c'), 'r').read()
expected = open(path_from_root('tests', 'fcntl-open', 'output.txt'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
+ self.do_run(src, expected, force_c=True, extra_emscripten_args=['-H', 'libc/fcntl.h'])
def test_fcntl_misc(self):
add_pre_run = '''
@@ -7087,49 +7125,8 @@ def process(filename):
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
def test_utime(self):
- add_pre_run_and_checks = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- \'\'\'
- var TEST_F1 = FS.createFolder('/', 'writeable', true, true);
- var TEST_F2 = FS.createFolder('/', 'unwriteable', true, false);
- \'\'\'
- ).replace(
- '// {{POST_RUN_ADDITIONS}}',
- \'\'\'
- Module.print('first changed: ' + (TEST_F1.timestamp == 1200000000000));
- Module.print('second changed: ' + (TEST_F2.timestamp == 1200000000000));
- \'\'\'
- )
- open(filename, 'w').write(src)
-'''
- src = r'''
- #include <stdio.h>
- #include <errno.h>
- #include <utime.h>
-
- int main() {
- struct utimbuf t = {1000000000, 1200000000};
- char* writeable = "/writeable";
- char* unwriteable = "/unwriteable";
-
- utime(writeable, &t);
- printf("writeable errno: %d\n", errno);
-
- utime(unwriteable, &t);
- printf("unwriteable errno: %d\n", errno);
-
- return 0;
- }
- '''
- expected = '''
- writeable errno: 0
- unwriteable errno: 1
- first changed: true
- second changed: false
- '''
- self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected), post_build=add_pre_run_and_checks)
+ src = open(path_from_root('tests', 'utime', 'test_utime.c'), 'r').read()
+ self.do_run(src, 'success', force_c=True)
def test_utf(self):
self.banned_js_engines = [SPIDERMONKEY_ENGINE] # only node handles utf well
@@ -7221,38 +7218,6 @@ def process(filename):
Settings.LINKABLE = linkable # regression check for issue #273
self.do_run(src, "1 2 3")
- def test_readdir(self):
- add_pre_run = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createFolder('', 'test', true, true);\\nFS.createLazyFile( 'test', 'some_file', 'http://localhost/some_file', true, false);\\nFS.createFolder('test', 'some_directory', true, true);"
- )
- open(filename, 'w').write(src)
-'''
-
- src = '''
- #include <dirent.h>
- #include <stdio.h>
-
- int main()
- {
- DIR * dir;
- dirent * entity;
-
- dir = opendir( "test" );
-
- while( ( entity = readdir( dir ) ) )
- {
- printf( "%s is a %s\\n", entity->d_name, entity->d_type & DT_DIR ? "directory" : "file" );
- }
-
- return 0;
- }
-
- '''
- self.do_run(src, ". is a directory\n.. is a directory\nsome_file is a file\nsome_directory is a directory", post_build=add_pre_run)
-
def test_fs_base(self):
Settings.INCLUDE_FULL_LIBRARY = 1
try:
@@ -7309,18 +7274,8 @@ def process(filename):
self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/unistd.h'])
def test_unistd_ttyname(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', 'ttyname.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
src = open(path_from_root('tests', 'unistd', 'ttyname.c'), 'r').read()
- expected = open(path_from_root('tests', 'unistd', 'ttyname.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, 'success', force_c=True)
def test_unistd_dup(self):
src = open(path_from_root('tests', 'unistd', 'dup.c'), 'r').read()
@@ -7352,18 +7307,8 @@ def process(filename):
self.do_run(src, expected)
def test_unistd_isatty(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', 'isatty.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
src = open(path_from_root('tests', 'unistd', 'isatty.c'), 'r').read()
- expected = open(path_from_root('tests', 'unistd', 'isatty.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, 'success', force_c=True)
def test_unistd_sysconf(self):
src = open(path_from_root('tests', 'unistd', 'sysconf.c'), 'r').read()
@@ -10020,97 +9965,97 @@ finalizing 3 (global == 0)
''')
# Generate tests for everything
- def make_run(fullname, name=-1, compiler=-1, embetter=0, quantum_size=0, typed_arrays=0, emcc_args=None, env='{}'):
- exec('''
-class %s(T):
- run_name = '%s'
- env = %s
+ def make_run(fullname, name=-1, compiler=-1, embetter=0, quantum_size=0,
+ typed_arrays=0, emcc_args=None, env=None):
- def tearDown(self):
- super(%s, self).tearDown()
+ if env is None: env = {}
- for k, v in self.env.iteritems():
- del os.environ[k]
+ TT = type(fullname, (T,), dict(run_name = fullname, env = env))
+
+ def tearDown(self):
+ super(TT, self).tearDown()
+
+ for k, v in self.env.iteritems():
+ del os.environ[k]
+
+ TT.tearDown = tearDown
+
+ def setUp(self):
+ super(TT, self).setUp()
+ for k, v in self.env.iteritems():
+ assert k not in os.environ, k + ' should not be in environment'
+ os.environ[k] = v
+
+ global checked_sanity
+ if not checked_sanity:
+ print '(checking sanity from test runner)' # do this after we set env stuff
+ check_sanity(force=True)
+ checked_sanity = True
+
+ Building.COMPILER_TEST_OPTS = ['-g']
+ os.chdir(self.get_dir()) # Ensure the directory exists and go there
+ Building.COMPILER = compiler
+
+ self.emcc_args = None if emcc_args is None else emcc_args[:]
+ if self.emcc_args is not None:
+ Settings.load(self.emcc_args)
+ Building.LLVM_OPTS = 0
+ if '-O2' in self.emcc_args:
+ Building.COMPILER_TEST_OPTS = [] # remove -g in -O2 tests, for more coverage
+ #Building.COMPILER_TEST_OPTS += self.emcc_args
+ for arg in self.emcc_args:
+ if arg.startswith('-O'):
+ Building.COMPILER_TEST_OPTS.append(arg) # so bitcode is optimized too, this is for cpp to ll
+ else:
+ try:
+ key, value = arg.split('=')
+ Settings[key] = value # forward -s K=V
+ except:
+ pass
+ return
+
+ # TODO: Move much of these to a init() function in shared.py, and reuse that
+ Settings.USE_TYPED_ARRAYS = typed_arrays
+ Settings.INVOKE_RUN = 1
+ Settings.RELOOP = 0 # we only do them in the "o2" pass
+ Settings.MICRO_OPTS = embetter
+ Settings.QUANTUM_SIZE = quantum_size
+ Settings.ASSERTIONS = 1-embetter
+ Settings.SAFE_HEAP = 1-embetter
+ Settings.CHECK_OVERFLOWS = 1-embetter
+ Settings.CORRECT_OVERFLOWS = 1-embetter
+ Settings.CORRECT_SIGNS = 0
+ Settings.CORRECT_ROUNDINGS = 0
+ Settings.CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = CORRECT_ROUNDINGS_LINES = SAFE_HEAP_LINES = []
+ Settings.CHECK_SIGNS = 0 #1-embetter
+ Settings.RUNTIME_TYPE_INFO = 0
+ Settings.DISABLE_EXCEPTION_CATCHING = 0
+ Settings.INCLUDE_FULL_LIBRARY = 0
+ Settings.BUILD_AS_SHARED_LIB = 0
+ Settings.RUNTIME_LINKED_LIBS = []
+ Settings.EMULATE_UNALIGNED_ACCESSES = int(Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2)
+ Settings.DOUBLE_MODE = 1 if Settings.USE_TYPED_ARRAYS and Building.LLVM_OPTS == 0 else 0
+ Settings.PRECISE_I64_MATH = 0
+ Settings.NAMED_GLOBALS = 0 if not embetter else 1
+
+ TT.setUp = setUp
- def setUp(self):
- super(%s, self).setUp()
-
- for k, v in self.env.iteritems():
- assert k not in os.environ, k + ' should not be in environment'
- os.environ[k] = v
-
- global checked_sanity
- if not checked_sanity:
- print '(checking sanity from test runner)' # do this after we set env stuff
- check_sanity(force=True)
- checked_sanity = True
-
- Building.COMPILER_TEST_OPTS = ['-g']
- os.chdir(self.get_dir()) # Ensure the directory exists and go there
- Building.COMPILER = %r
-
- self.emcc_args = %s
- if self.emcc_args is not None:
- Settings.load(self.emcc_args)
- Building.LLVM_OPTS = 0
- if '-O2' in self.emcc_args:
- Building.COMPILER_TEST_OPTS = [] # remove -g in -O2 tests, for more coverage
- #Building.COMPILER_TEST_OPTS += self.emcc_args
- for arg in self.emcc_args:
- if arg.startswith('-O'):
- Building.COMPILER_TEST_OPTS.append(arg) # so bitcode is optimized too, this is for cpp to ll
- else:
- try:
- key, value = arg.split('=')
- Settings[key] = value # forward -s K=V
- except:
- pass
- return
-
- embetter = %d
- quantum_size = %d
- # TODO: Move much of these to a init() function in shared.py, and reuse that
- Settings.USE_TYPED_ARRAYS = %d
- Settings.INVOKE_RUN = 1
- Settings.RELOOP = 0 # we only do them in the "o2" pass
- Settings.MICRO_OPTS = embetter
- Settings.QUANTUM_SIZE = quantum_size
- Settings.ASSERTIONS = 1-embetter
- Settings.SAFE_HEAP = 1-embetter
- Settings.CHECK_OVERFLOWS = 1-embetter
- Settings.CORRECT_OVERFLOWS = 1-embetter
- Settings.CORRECT_SIGNS = 0
- Settings.CORRECT_ROUNDINGS = 0
- Settings.CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = CORRECT_ROUNDINGS_LINES = SAFE_HEAP_LINES = []
- Settings.CHECK_SIGNS = 0 #1-embetter
- Settings.RUNTIME_TYPE_INFO = 0
- Settings.DISABLE_EXCEPTION_CATCHING = 0
- Settings.INCLUDE_FULL_LIBRARY = 0
- Settings.BUILD_AS_SHARED_LIB = 0
- Settings.RUNTIME_LINKED_LIBS = []
- Settings.EMULATE_UNALIGNED_ACCESSES = int(Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2)
- Settings.DOUBLE_MODE = 1 if Settings.USE_TYPED_ARRAYS and Building.LLVM_OPTS == 0 else 0
- Settings.PRECISE_I64_MATH = 0
- Settings.NAMED_GLOBALS = 0 if not embetter else 1
-
-TT = %s
-''' % (fullname, fullname, env, fullname, fullname, compiler, str(emcc_args), embetter, quantum_size, typed_arrays, fullname))
return TT
# Make one run with the defaults
- exec('default = make_run("default", compiler=CLANG, emcc_args=[])')
+ default = make_run("default", compiler=CLANG, emcc_args=[])
# Make one run with -O1, with safe heap
- exec('o1 = make_run("o1", compiler=CLANG, emcc_args=["-O1", "-s", "ASM_JS=0", "-s", "SAFE_HEAP=1"])')
+ o1 = make_run("o1", compiler=CLANG, emcc_args=["-O1", "-s", "ASM_JS=0", "-s", "SAFE_HEAP=1"])
# Make one run with -O2, but without closure (we enable closure in specific tests, otherwise on everything it is too slow)
- exec('o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=0", "-s", "JS_CHUNK_SIZE=1024"])')
+ o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=0", "-s", "JS_CHUNK_SIZE=1024"])
# asm.js
- exec('asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1", "-s", "CHECK_HEAP_ALIGN=1"])')
- exec('asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"])')
- exec('asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1"])')
- exec('''asm2x86 = make_run("asm2x86", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "CHECK_HEAP_ALIGN=1"], env='{"EMCC_LLVM_TARGET": "i386-pc-linux-gnu"}')''')
+ asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1", "-s", "CHECK_HEAP_ALIGN=1"])
+ asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"])
+ asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1"])
+ asm2x86 = make_run("asm2x86", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "CHECK_HEAP_ALIGN=1"], env={"EMCC_LLVM_TARGET": "i386-pc-linux-gnu"})
# Make custom runs with various options
for compiler, quantum, embetter, typed_arrays in [
@@ -10120,7 +10065,7 @@ TT = %s
fullname = 's_0_%d%s%s' % (
embetter, '' if quantum == 4 else '_q' + str(quantum), '' if typed_arrays in [0, 1] else '_t' + str(typed_arrays)
)
- exec('%s = make_run(fullname, %r,%r,%d,%d,%d)' % (fullname, fullname, compiler, embetter, quantum, typed_arrays))
+ locals()[fullname] = make_run(fullname, fullname, compiler, embetter, quantum, typed_arrays)
del T # T is just a shape for the specific subclasses, we don't test it itself
@@ -13183,8 +13128,11 @@ Press any key to continue.'''
self.btest('sdl_maprgba.c', reference='sdl_maprgba.png', reference_slack=3)
def test_sdl_rotozoom(self):
- shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'example.png'))
- self.btest('sdl_rotozoom.c', reference='sdl_rotozoom.png', args=['--preload-file', 'example.png'])
+ shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png'))
+ self.btest('sdl_rotozoom.c', reference='sdl_rotozoom.png', args=['--preload-file', 'screenshot.png'], reference_slack=3)
+
+ def test_sdl_gfx_primitives(self):
+ self.btest('sdl_gfx_primitives.c', reference='sdl_gfx_primitives.png', reference_slack=1)
def test_sdl_canvas_palette_2(self):
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
diff --git a/tests/sdl_gfx_primitives.c b/tests/sdl_gfx_primitives.c
new file mode 100644
index 00000000..db0c6181
--- /dev/null
+++ b/tests/sdl_gfx_primitives.c
@@ -0,0 +1,46 @@
+#include "SDL/SDL.h"
+#include "SDL/SDL_gfxPrimitives.h"
+
+#ifdef EMSCRIPTEN
+#include "emscripten.h"
+#endif
+
+int main(int argc, char **argv) {
+ SDL_Init(SDL_INIT_VIDEO);
+
+ const int width = 400;
+ const int height = 400;
+ SDL_Surface *screen = SDL_SetVideoMode(width, height, 32, SDL_SWSURFACE);
+ boxColor(screen, 0, 0, width, height, 0xff);
+
+ boxColor(screen, 0, 0, 98, 98, 0xff0000ff);
+ boxRGBA(screen, 100, 0, 198, 98, 0, 0, 0xff, 0xff);
+ // check that the x2 > x1 case is handled correctly
+ boxColor(screen, 298, 98, 200, 0, 0x00ff00ff);
+ boxColor(screen, 398, 98, 300, 0, 0xff0000ff);
+
+ rectangleColor(screen, 0, 100, 98, 198, 0x000ffff);
+ rectangleRGBA(screen, 100, 100, 198, 198, 0xff, 0, 0, 0xff);
+
+ ellipseColor(screen, 300, 150, 99, 49, 0x00ff00ff);
+ filledEllipseColor(screen, 100, 250, 99, 49, 0x00ff00ff);
+ filledEllipseRGBA(screen, 250, 300, 49, 99, 0, 0, 0xff, 0xff);
+
+ lineColor(screen, 300, 200, 400, 300, 0x00ff00ff);
+ lineRGBA(screen, 300, 300, 400, 400, 0, 0xff, 0, 0xff);
+
+ SDL_UpdateRect(screen, 0, 0, 0, 0);
+
+#ifndef EMSCRIPTEN
+ SDL_Event evt;
+ SDL_SaveBMP(screen, "native_output.bmp");
+ while (1) {
+ if (SDL_PollEvent(&evt) != 0 && evt.type == SDL_QUIT) break;
+ SDL_Delay(33);
+ }
+#endif
+
+ SDL_Quit();
+
+ return 1;
+}
diff --git a/tests/sdl_gfx_primitives.png b/tests/sdl_gfx_primitives.png
new file mode 100644
index 00000000..525b4f8f
--- /dev/null
+++ b/tests/sdl_gfx_primitives.png
Binary files differ
diff --git a/tests/sdl_rotozoom.c b/tests/sdl_rotozoom.c
index b3970f6c..e81258d9 100644
--- a/tests/sdl_rotozoom.c
+++ b/tests/sdl_rotozoom.c
@@ -6,15 +6,18 @@
#include "emscripten.h"
#endif
+const int numSprites = 8;
SDL_Surface *screen;
-SDL_Surface *sprite[6];
+SDL_Surface *sprite[numSprites];
void mainloop() {
int i;
+ int row = 0;
SDL_Rect rect = { 0, 0, 100, 100 };
- for (i = 0; i < 6; i++) {
+ for (i = 0; i < numSprites; i++) {
rect.x = i & 1 ? 200 : 0;
- rect.y = i & 2 ? 200 : 0;
+ rect.y = row * 200;
+ row += i & 1;
SDL_BlitSurface(sprite[i], 0, screen, &rect);
SDL_UpdateRect(screen, 0, 0, 0, 0);
}
@@ -23,20 +26,27 @@ void mainloop() {
int main(int argc, char **argv) {
SDL_Init(SDL_INIT_VIDEO);
- screen = SDL_SetVideoMode(400, 400, 32, SDL_SWSURFACE);
+ const int width = 400;
+ const int height = 200 * (numSprites + 1) / 2;
+ screen = SDL_SetVideoMode(width, height, 32, SDL_SWSURFACE);
+ SDL_Rect rect = { 0, 0, width, height };
+ SDL_FillRect(screen, &rect, SDL_MapRGBA(screen->format, 0, 0, 0, 0xff));
- sprite[0] = IMG_Load("example.png");
+ sprite[0] = IMG_Load("screenshot.png");
sprite[1] = SDL_CreateRGBSurface(SDL_SWSURFACE, 100, 100, 32, 0xFF000000, 0xFF0000, 0xFF00, 0xFF);
SDL_FillRect(sprite[1], 0, 0xA0A0A0A0);
sprite[2] = zoomSurface(sprite[0], 0.5, 0.5, SMOOTHING_ON);
sprite[3] = zoomSurface(sprite[1], 0.5, 0.5, SMOOTHING_ON);
sprite[4] = rotozoomSurface(sprite[0], -20, 0.3, SMOOTHING_ON);
sprite[5] = rotozoomSurface(sprite[1], 45, 0.5, SMOOTHING_ON);
+ sprite[6] = zoomSurface(sprite[0], -0.5, 0.5, SMOOTHING_ON);
+ sprite[7] = zoomSurface(sprite[0], -0.5, -0.5, SMOOTHING_ON);
mainloop();
#ifndef EMSCRIPTEN
SDL_Event evt;
+ SDL_SaveBMP(screen, "native_output.bmp");
while (1) {
if (SDL_PollEvent(&evt) != 0 && evt.type == SDL_QUIT) break;
//mainloop();
diff --git a/tests/sdl_rotozoom.png b/tests/sdl_rotozoom.png
index 3ca8da70..5c5dec2f 100644
--- a/tests/sdl_rotozoom.png
+++ b/tests/sdl_rotozoom.png
Binary files differ
diff --git a/tests/unistd/isatty.c b/tests/unistd/isatty.c
index cc1ff641..191036e9 100644
--- a/tests/unistd/isatty.c
+++ b/tests/unistd/isatty.c
@@ -1,28 +1,28 @@
-#include <stdio.h>
+#include <assert.h>
#include <errno.h>
-#include <unistd.h>
#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
int main() {
- printf("read: %d\n", isatty(open("/read", O_RDONLY)));
- printf("errno: %d\n", errno);
- errno = 0;
+ int err;
+
+ err = isatty(-1);
+ assert(!err);
+ assert(errno == EBADF);
- printf("write: %d\n", isatty(open("/write", O_WRONLY)));
- printf("errno: %d\n", errno);
- errno = 0;
+ err = isatty(open("/dev/stdin", O_RDONLY));
+ assert(err == 1);
- printf("all: %d\n", isatty(open("/all", O_RDONLY)));
- printf("errno: %d\n", errno);
- errno = 0;
+ err = isatty(open("/dev/null", O_RDONLY));
+ assert(!err);
- printf("folder: %d\n", isatty(open("/folder", O_RDONLY)));
- printf("errno: %d\n", errno);
- errno = 0;
+ err = isatty(open("/dev", O_RDONLY));
+ assert(!err);
- printf("file: %d\n", isatty(open("/file", O_RDONLY)));
- printf("errno: %d\n", errno);
- errno = 0;
+ puts("success");
- return 0;
-}
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/tests/unistd/isatty.js b/tests/unistd/isatty.js
deleted file mode 100644
index d88bd2be..00000000
--- a/tests/unistd/isatty.js
+++ /dev/null
@@ -1,5 +0,0 @@
-FS.createDevice('/', 'read', function() {}, null);
-FS.createDevice('/', 'write', null, function() {});
-FS.createDevice('/', 'all', function() {}, function() {});
-FS.createFolder('/', 'folder', true, true);
-FS.createDataFile('/', 'file', 'test', true, true);
diff --git a/tests/unistd/isatty.out b/tests/unistd/isatty.out
deleted file mode 100644
index 6823c1f0..00000000
--- a/tests/unistd/isatty.out
+++ /dev/null
@@ -1,10 +0,0 @@
-read: 0
-errno: 25
-write: 0
-errno: 25
-all: 0
-errno: 25
-folder: 0
-errno: 25
-file: 0
-errno: 25
diff --git a/tests/unistd/ttyname.c b/tests/unistd/ttyname.c
index 7080be5d..b7cc27bd 100644
--- a/tests/unistd/ttyname.c
+++ b/tests/unistd/ttyname.c
@@ -1,51 +1,43 @@
-#include <stdio.h>
+#include <assert.h>
#include <errno.h>
-#include <unistd.h>
#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
int main() {
+ int err;
+ int stdin, null, dev;
char buffer[256];
- int d = open("/device", O_RDWR);
- int f = open("/", O_RDONLY);
char* result;
+ stdin = open("/dev/stdin", O_RDONLY);
+ null = open("/dev/null", O_RDONLY);
+ dev = open("/dev", O_RDONLY);
+
result = ctermid(buffer);
- if (result) {
- printf("ctermid: %s\n", result);
- } else {
- printf("ctermid errno: %d\n", errno);
- errno = 0;
- }
-
- if (ttyname_r(d, buffer, 256) == 0) {
- printf("ttyname_r(d, ..., 256): %s\n", buffer);
- } else {
- printf("ttyname_r(d, ..., 256) errno: %d\n", errno);
- errno = 0;
- }
-
- if (ttyname_r(d, buffer, 2) == 0) {
- printf("ttyname_r(d, ..., 2): %s\n", buffer);
- } else {
- printf("ttyname_r(d, ..., 2) errno: %d\n", errno);
- errno = 0;
- }
-
- result = ttyname(d);
- if (result) {
- printf("ttyname(d): %s\n", result);
- } else {
- printf("ttyname(d) errno: %d\n", errno);
- errno = 0;
- }
-
- result = ttyname(f);
- if (result) {
- printf("ttyname(f): %s\n", result);
- } else {
- printf("ttyname(f) errno: %d\n", errno);
- errno = 0;
- }
-
- return 0;
+ assert(!strcmp(result, "/dev/tty"));
+
+ // strstr instead of strcmp as native code may
+ // be using a virtual console (e.g. /dev/tty02)
+ err = ttyname_r(stdin, buffer, 256);
+ assert(!err);
+ assert(strstr(buffer, "/dev/tty"));
+
+ err = ttyname_r(stdin, buffer, 2);
+ assert(err == ERANGE);
+
+ result = ttyname(stdin);
+ assert(strstr(result, "/dev/tty"));
+
+ result = ttyname(null);
+ assert(!result);
+
+ result = ttyname(dev);
+ assert(!result);
+
+ puts("success");
+
+ return EXIT_SUCCESS;
}
diff --git a/tests/unistd/ttyname.js b/tests/unistd/ttyname.js
deleted file mode 100644
index a21f417f..00000000
--- a/tests/unistd/ttyname.js
+++ /dev/null
@@ -1 +0,0 @@
-FS.createDevice('/', 'device', function() {}, function() {});
diff --git a/tests/unistd/ttyname.out b/tests/unistd/ttyname.out
deleted file mode 100644
index b47cbf75..00000000
--- a/tests/unistd/ttyname.out
+++ /dev/null
@@ -1,5 +0,0 @@
-ctermid: /dev/tty
-ttyname_r(d, ..., 256): /device
-ttyname_r(d, ..., 2) errno: 34
-ttyname(d): /device
-ttyname(f) errno: 25
diff --git a/tests/utime/test_utime.c b/tests/utime/test_utime.c
new file mode 100644
index 00000000..1793f4a5
--- /dev/null
+++ b/tests/utime/test_utime.c
@@ -0,0 +1,53 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utime.h>
+#include <sys/stat.h>
+
+void setup() {
+ mkdir("writeable", 0777);
+ mkdir("unwriteable", 0111);
+}
+
+void cleanup() {
+ rmdir("writeable");
+ rmdir("unwriteable");
+}
+
+void test() {
+ struct stat s;
+ // currently, the most recent timestamp is shared for atime,
+ // ctime and mtime. using unique values for each in the test
+ // will fail
+ struct utimbuf t = {1000000000, 1000000000};
+
+ utime("writeable", &t);
+ assert(!errno);
+ memset(&s, 0, sizeof s);
+ stat("writeable", &s);
+ assert(s.st_atime == t.actime);
+ assert(s.st_mtime == t.modtime);
+
+ // write permissions aren't checked when setting node
+ // attributes unless the user uid isn't the owner (so
+ // therefor, this should work fine)
+ utime("unwriteable", &t);
+ assert(!errno);
+ memset(&s, 0, sizeof s);
+ stat("unwriteable", &s);
+ assert(s.st_atime == t.actime);
+ assert(s.st_mtime == t.modtime);
+
+ puts("success");
+}
+
+int main() {
+ atexit(cleanup);
+ signal(SIGABRT, cleanup);
+ setup();
+ test();
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 06d82752..71a59921 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -136,6 +136,9 @@ var CONTROL_FLOW = set('do', 'while', 'for', 'if', 'switch');
var NAME_OR_NUM = set('name', 'num');
var ASSOCIATIVE_BINARIES = set('+', '*', '|', '&', '^');
+var BREAK_CAPTURERS = set('do', 'while', 'for', 'switch');
+var CONTINUE_CAPTURERS = LOOP;
+
var NULL_NODE = ['name', 'null'];
var UNDEFINED_NODE = ['unary-prefix', 'void', ['num', 0]];
var TRUE_NODE = ['unary-prefix', '!', ['num', 0]];
@@ -559,7 +562,8 @@ function simplifyExpressionsPre(ast) {
} else if (type === 'binary' && node[1] === '^') {
// LLVM represents bitwise not as xor with -1. Translate it back to an actual bitwise not.
if (node[3][0] === 'unary-prefix' && node[3][1] === '-' && node[3][2][0] === 'num' &&
- node[3][2][1] === 1) {
+ node[3][2][1] === 1 &&
+ !(node[2][0] == 'unary-prefix' && node[2][1] == '~')) { // avoid creating ~~~ which is confusing for asm given the role of ~~
return ['unary-prefix', '~', node[2]];
}
} else if (type === 'binary' && node[1] === '>>' && node[3][0] === 'num' &&
@@ -633,7 +637,8 @@ function simplifyExpressionsPre(ast) {
if (node[0] === 'seq' && node[1][0] === 'assign' && node[1][2][0] === 'sub' && node[1][2][1][0] === 'name' &&
(node[1][2][1][1] === 'HEAP32' || node[1][2][1][1] === 'HEAPF32') &&
node[1][2][2][0] === 'binary' && node[1][2][2][2][0] === 'name' && node[1][2][2][2][1] === 'tempDoublePtr' &&
- node[1][3][0] === 'sub' && node[1][3][1][0] === 'name' && (node[1][3][1][1] === 'HEAP32' || node[1][3][1][1] === 'HEAPF32')) {
+ node[1][3][0] === 'sub' && node[1][3][1][0] === 'name' && (node[1][3][1][1] === 'HEAP32' || node[1][3][1][1] === 'HEAPF32') &&
+ node[2][0] !== 'seq') { // avoid (x, y, z) which can be used for tempDoublePtr on doubles for alignment fixes
if (node[1][2][1][1] === 'HEAP32') {
node[1][3][1][1] = 'HEAPF32';
return ['unary-prefix', '+', node[1][3]];
@@ -1531,10 +1536,15 @@ function unVarify(vars, ret) { // transform var x=1, y=2 etc. into (x=1, y=2), i
var ASM_INT = 0;
var ASM_DOUBLE = 1;
-function detectAsmCoercion(node) {
+function detectAsmCoercion(node, asmInfo) {
// for params, +x vs x|0, for vars, 0.0 vs 0
if (node[0] === 'num' && node[1].toString().indexOf('.') >= 0) return ASM_DOUBLE;
- return node[0] === 'unary-prefix' ? ASM_DOUBLE : ASM_INT;
+ if (node[0] === 'unary-prefix') return ASM_DOUBLE;
+ if (asmInfo && node[0] == 'name') {
+ if (node[1] in asmInfo.vars) return asmInfo.vars[node[1]];
+ if (node[1] in asmInfo.params) return asmInfo.params[node[1]];
+ }
+ return ASM_INT;
}
function makeAsmParamCoercion(param, type) {
@@ -2974,17 +2984,31 @@ function outline(ast) {
for (var i = 0; i < stack.length; i++) {
asmData.stackPos[stack[i]] = i*8;
}
-
+ // Reserve an extra two spots: one for control flow var, the other for control flow data
+ asmData.stackSize = (stack.length + 2)*8;
+ asmData.controlStackPos = asmData.stackSize - 16;
+ asmData.controlDataStackPos = asmData.stackSize - 8;
asmData.splitCounter = 0;
}
// Analyze uses - reads and writes - of variables in part of the AST of a function
function analyzeCode(func, asmData, ast) {
+ var labels = {};
+ var labelCounter = 1; // 0 means no label
+
+ traverse(ast, function(node, type) {
+ if ((type == 'label' || type in LOOP_FLOW) && node[1] && !(node[1] in labels)) {
+ labels[node[1]] = labelCounter++;
+ }
+ });
+
var writes = {};
var appearances = {};
var hasReturn = false, hasBreak = false, hasContinue = false;
var breaks = {}; // set of labels we break or continue
var continues = {}; // to. '0' is an unlabeled one
+ var breakCapturers = 0;
+ var continueCapturers = 0;
traverse(ast, function(node, type) {
if (type == 'assign' && node[2][0] == 'name') {
@@ -3001,11 +3025,31 @@ function outline(ast) {
} else if (type == 'return') {
hasReturn = true;
} else if (type == 'break') {
- breaks[node[1] || 0] = 0;
+ var label = node[1] || 0;
+ if (!label && breakCapturers > 0) return; // no label, and captured
+ if (label && (label in labels)) return; // label, and defined in this code, so captured
+ breaks[label || 0] = 0;
hasBreak = true;
} else if (type == 'continue') {
- continues[node[1] || 0] = 0;
+ var label = node[1] || 0;
+ if (!label && continueCapturers > 0) return; // no label, and captured
+ if (label && (label in labels)) return; // label, and defined in this code, so captured
+ continues[label || 0] = 0;
hasContinue = true;
+ } else {
+ if (type in BREAK_CAPTURERS) {
+ breakCapturers++;
+ }
+ if (type in CONTINUE_CAPTURERS) {
+ continueCapturers++;
+ }
+ }
+ }, function(node, type) {
+ if (type in BREAK_CAPTURERS) {
+ breakCapturers--;
+ }
+ if (type in CONTINUE_CAPTURERS) {
+ continueCapturers--;
}
});
@@ -3015,9 +3059,26 @@ function outline(ast) {
if (appearances[name] > 0) reads[name] = 0;
}
- return { writes: writes, reads: reads, hasReturn: hasReturn, breaks: breaks, continues: continues };
+ return { writes: writes, reads: reads, hasReturn: hasReturn, breaks: breaks, continues: continues, labels: labels };
+ }
+
+ function makeAssign(dst, src) {
+ return ['assign', true, dst, src];
+ }
+ function makeStackAccess(type, pos) { // TODO: float64, not 32
+ return ['sub', ['name', type == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', pos]], ['num', '2']]];
+ }
+ function makeIf(cond, then, else_) {
+ var ret = ['if', cond, ['block', then]];
+ if (else_) ret.push(['block', else_]);
+ return ret;
+ }
+ function makeComparison(left, comp, right) {
+ return ['binary', comp, left, right];
}
+ var CONTROL_BREAK = 1, CONTROL_BREAK_LABEL = 2, CONTROL_CONTINUE = 3, CONTROL_CONTINUE_LABEL = 4, CONTROL_RETURN_VOID = 5, CONTROL_RETURN_INT = 6, CONTROL_RETURN_DOUBLE = 7;
+
var sizeToOutline = extraInfo.sizeToOutline;
var level = 0;
@@ -3025,7 +3086,7 @@ function outline(ast) {
printErr(' do outline ' + [func[1], level, 'range:', start, end, 'of', stats.length]);
var code = stats.slice(start, end+1);
var newIdent = func[1] + '$' + (asmData.splitCounter++);
- // add spills and reads before and after the call to the outlined code
+ // add spills and reads before and after the call to the outlined code, and in the outlined code itself
var codeInfo = analyzeCode(func, asmData, code);
var reps = [];
for (var v in codeInfo.reads) {
@@ -3039,21 +3100,109 @@ function outline(ast) {
reps.push(['stat', ['assign', true, ['name', v], ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]]]]);
code.push(['stat', ['assign', true, ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]);
}
- stats.splice.apply(stats, [start, end-start+1].concat(reps));
// Generate new function
if (codeInfo.hasReturn || codeInfo.hasBreak || codeInfo.hasContinue) {
// we need to capture all control flow using a top-level labeled one-time loop in the outlined function
code = [['label', 'OL', ['do', ['num', 0], ['block', code]]]];
+ var breakCapturers = 0;
+ var continueCapturers = 0;
+ traverse(code, function(node, type) {
+ // replace all break/continue/returns with code to break out of the main one-time loop, and set the control data
+ if (type == 'return') {
+ var ret = ['break', 'OL'];
+ if (!node[1]) {
+ ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', CONTROL_RETURN_VOID]), ret];
+ } else {
+ var type = detectAsmCoercion(node[1], asmData);
+ ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', type == ASM_INT ? CONTROL_RETURN_INT : CONTROL_RETURN_DOUBLE]), ret];
+ ret = ['seq', makeAssign(makeStackAccess(type, asmData.controlDataStackPos), node[1]), ret];
+ }
+ return ret;
+ } else if (type == 'break') {
+ var label = node[1] || 0;
+ if (label == 'OL') return; // this was just added before us, it is new replacement code
+ if (!label && breakCapturers > 0) return; // no label, and captured
+ if (label && (label in codeInfo.labels)) return; // label, and defined in this code, so captured
+ var ret = ['break', 'OL'];
+ ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', label ? CONTROL_BREAK_LABEL : CONTROL_BREAK]), ret];
+ if (label) {
+ assert(label in codeInfo.labels, label + ' in ' + keys(codeInfo.labels));
+ ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos), ['num', codeInfo.labels[label]]), ret];
+ }
+ return ret;
+ } else if (type == 'continue') {
+ var label = node[1] || 0;
+ if (!label && continueCapturers > 0) return; // no label, and captured
+ if (label && (label in codeInfo.labels)) return; // label, and defined in this code, so captured
+ var ret = ['break', 'OL'];
+ ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', label ? CONTROL_CONTINUE_LABEL : CONTROL_CONTINUE]), ret];
+ if (label) {
+ ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos), ['num', codeInfo.labels[label]]), ret];
+ }
+ return ret;
+ } else {
+ if (type in BREAK_CAPTURERS) {
+ breakCapturers++;
+ }
+ if (type in CONTINUE_CAPTURERS) {
+ continueCapturers++;
+ }
+ }
+ }, function(node, type) {
+ if (type in BREAK_CAPTURERS) {
+ breakCapturers--;
+ }
+ if (type in CONTINUE_CAPTURERS) {
+ continueCapturers--;
+ }
+ });
+ // read the control data at the callsite to the outlined function
+ if (codeInfo.hasReturn) {
+ reps.push(makeIf(
+ makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_RETURN_VOID]),
+ [['stat', ['return']]]
+ ));
+ reps.push(makeIf(
+ makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_RETURN_INT]),
+ [['stat', ['return', makeStackAccess(ASM_INT, asmData.controlDataStackPos)]]]
+ ));
+ reps.push(makeIf(
+ makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_RETURN_DOUBLE]),
+ [['stat', ['return', makeStackAccess(ASM_DOUBLE, asmData.controlDataStackPos)]]]
+ ));
+ }
+ if (codeInfo.hasBreak) {
+ reps.push(makeIf(
+ makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_BREAK]),
+ ['stat', ['break']]
+ ));
+ reps.push(makeIf(
+ makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_BREAK_LABEL]),
+ ['stat', ['break', makeStackAccess(ASM_INT, asmData.controlDataStackPos)]] // XXX here and below, need a switch overall possible labels
+ ));
+ }
+ if (codeInfo.hasContinue) {
+ reps.push(makeIf(
+ makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_CONTINUE]),
+ ['stat', ['break']]
+ ));
+ reps.push(makeIf(
+ makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_CONTINUE_LABEL]),
+ ['stat', ['continue', makeStackAccess(ASM_INT, asmData.controlDataStackPos)]] // XXX
+ ));
+ }
}
var newFunc = ['defun', newIdent, ['sp'], code];
- var newAsmInfo = { params: { sp: ASM_INT }, vars: {} };
+ var newAsmData = { params: { sp: ASM_INT }, vars: {} };
for (var v in codeInfo.reads) {
- newAsmInfo.vars[v] = getAsmType(asmData, v);
+ newAsmData.vars[v] = getAsmType(asmData, v);
}
for (var v in codeInfo.writes) {
- newAsmInfo.vars[v] = getAsmType(asmData, v);
+ newAsmData.vars[v] = getAsmType(asmData, v);
}
- denormalizeAsm(newFunc, newAsmInfo);
+ denormalizeAsm(newFunc, newAsmData);
+ // replace in stats
+ stats.splice.apply(stats, [start, end-start+1].concat(reps));
return [newFunc];
}
@@ -3138,7 +3287,7 @@ function outline(ast) {
if (newFuncs.length > 0) {
// We have outlined. Add stack support: header in which we allocate enough stack space TODO
- // If sp was not present before, add it and before each return, pop the stack TODO
+ // If sp was not present before, add it and before each return, pop the stack. also a final pop if not ending with a return TODO
// (none of this should be done in inner functions, of course, just the original)
// add new functions to the toplevel, or create a toplevel if there isn't one
@@ -3175,12 +3324,13 @@ function fixDotZero(js) {
});
}
-function asmLoopOptimizer(ast) {
+function asmLastOpts(ast) {
traverseGeneratedFunctions(ast, function(fun) {
- // This is at the end of the pipeline, we can assume all other optimizations are done, and we modify loops
- // into shapes that might confuse other passes
traverse(fun, function(node, type) {
if (type === 'while' && node[1][0] === 'num' && node[1][1] === 1 && node[2][0] === 'block') {
+ // This is at the end of the pipeline, we can assume all other optimizations are done, and we modify loops
+ // into shapes that might confuse other passes
+
// while (1) { .. if (..) { break } } ==> do { .. } while(..)
var stats = node[2][1];
var last = stats[stats.length-1];
@@ -3208,6 +3358,11 @@ function asmLoopOptimizer(ast) {
node[1] = simplifyNotCompsDirect(['unary-prefix', '!', conditionToBreak]);
return node;
}
+ } else if (type == 'binary' && node[1] == '&' && node[3][0] == 'unary-prefix' && node[3][1] == '-' && node[3][2][0] == 'num' && node[3][2][1] == 1) {
+ // Change &-1 into |0, at this point the hint is no longer needed
+ node[1] = '|';
+ node[3] = node[3][2];
+ node[3][1] = 0;
}
});
});
@@ -3266,7 +3421,7 @@ arguments_.slice(1).forEach(function(arg) {
passes[arg](ast);
});
if (asm && last) {
- asmLoopOptimizer(ast); // TODO: move out of last, to make last faster when done later (as in side modules)
+ asmLastOpts(ast); // TODO: move out of last, to make last faster when done later (as in side modules)
prepDotZero(ast);
}
var js = astToSrc(ast, minifyWhitespace), old;
diff --git a/tools/shared.py b/tools/shared.py
index 46245fd1..0351a736 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -257,7 +257,7 @@ def check_clang_version():
def check_llvm_version():
try:
- check_clang_version();
+ check_clang_version()
except Exception, e:
logging.warning('Could not verify LLVM version: %s' % str(e))
@@ -1196,7 +1196,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
opts.append('-jump-threading')
opts.append('-correlated-propagation')
opts.append('-dse')
- #addExtensionsToPM(EP_ScalarOptimizerLate, MPM);
+ #addExtensionsToPM(EP_ScalarOptimizerLate, MPM)
opts.append('-adce')
opts.append('-simplifycfg')
@@ -1300,7 +1300,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
'-O' + str(opt_level), '--closure', '0'], raw)
f = open(relooper, 'w')
f.write("// Relooper, (C) 2012 Alon Zakai, MIT license, https://github.com/kripken/Relooper\n")
- f.write("var Relooper = (function() {\n");
+ f.write("var Relooper = (function() {\n")
f.write(open(raw).read())
f.write('\n return Module.Relooper;\n')
f.write('})();\n')
diff --git a/tools/test-js-optimizer-asm-last-output.js b/tools/test-js-optimizer-asm-last-output.js
index 0f95d544..f850b18f 100644
--- a/tools/test-js-optimizer-asm-last-output.js
+++ b/tools/test-js-optimizer-asm-last-output.js
@@ -30,6 +30,7 @@ function finall(x) {
a = -999999984306749400.0;
a = -999999984306749400.0;
a = -0xde0b6b000000000;
+ f(g() | 0);
return 12.0e10;
}
function looop() {
diff --git a/tools/test-js-optimizer-asm-last.js b/tools/test-js-optimizer-asm-last.js
index 05e1049e..1d39b1a6 100644
--- a/tools/test-js-optimizer-asm-last.js
+++ b/tools/test-js-optimizer-asm-last.js
@@ -30,6 +30,7 @@ function finall(x) {
a = +-0xde0b6b000000000;
a = -+0xde0b6b000000000;
a = -0xde0b6b000000000;
+ f(g() & -1);
return +12e10;
}
function looop() {
diff --git a/tools/test-js-optimizer-asm-pre-output.js b/tools/test-js-optimizer-asm-pre-output.js
index b7afab26..8a5803d6 100644
--- a/tools/test-js-optimizer-asm-pre-output.js
+++ b/tools/test-js-optimizer-asm-pre-output.js
@@ -66,6 +66,7 @@ function b($this, $__n) {
HEAP32[$4] = ~HEAP32[$5];
HEAP32[$4] = ~HEAP32[$5];
HEAP32[$4] = ~HEAP32[$5];
+ h(~~g ^ -1);
return;
}
function rett() {
@@ -156,6 +157,8 @@ function tempDoublePtr($45, $14, $28, $42) {
unelim2 = 127 + $14 | 0;
func();
HEAP32[4] = unelim2;
+ barrier();
+ $f163 = (HEAP32[tempDoublePtr >> 2] = HEAP32[$f165 >> 2], HEAP32[tempDoublePtr + 4 >> 2] = HEAP32[$f165 + 4 >> 2], +HEAPF64[tempDoublePtr >> 3]);
}
function boxx($this, $aabb, $xf, $childIndex) {
$this = $this | 0;
diff --git a/tools/test-js-optimizer-asm-pre.js b/tools/test-js-optimizer-asm-pre.js
index b762a60e..5c004342 100644
--- a/tools/test-js-optimizer-asm-pre.js
+++ b/tools/test-js-optimizer-asm-pre.js
@@ -70,6 +70,7 @@ function b($this, $__n) {
HEAP32[$4] = HEAP32[$5]^-1;
// Rewrite to ~ and eliminate the |0.
HEAP32[$4] = ((HEAP32[$5]|0)^-1)|0;
+ h((~~g) ^ -1); // do NOT convert this, as it would lead to ~~~ which is confusing in asm, given the role of ~~
return;
}
function rett() {
@@ -166,6 +167,8 @@ function tempDoublePtr($45, $14, $28, $42) {
unelim2 = (HEAP32[tempDoublePtr >> 2] = 127 + $14, +HEAPF32[tempDoublePtr >> 2]);
func();
HEAPF32[4] = unelim2;
+ barrier();
+ $f163 = (HEAP32[tempDoublePtr >> 2] = HEAP32[$f165 >> 2], HEAP32[tempDoublePtr + 4 >> 2] = HEAP32[$f165 + 4 >> 2], +HEAPF64[tempDoublePtr >> 3]);
}
function boxx($this, $aabb, $xf, $childIndex) {
$this = $this | 0;