diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 45 | ||||
-rw-r--r-- | src/embind/embind.js | 26 | ||||
-rw-r--r-- | src/embind/emval.js | 15 | ||||
-rw-r--r-- | src/intertyper.js | 6 | ||||
-rw-r--r-- | src/jsifier.js | 11 | ||||
-rw-r--r-- | src/library.js | 3968 | ||||
-rw-r--r-- | src/library_browser.js | 31 | ||||
-rw-r--r-- | src/library_egl.js | 9 | ||||
-rw-r--r-- | src/library_gl.js | 17 | ||||
-rw-r--r-- | src/library_glut.js | 2 | ||||
-rw-r--r-- | src/library_jansson.js | 2 | ||||
-rw-r--r-- | src/library_openal.js | 596 | ||||
-rw-r--r-- | src/library_path.js | 134 | ||||
-rw-r--r-- | src/library_sdl.js | 266 | ||||
-rw-r--r-- | src/modules.js | 2 | ||||
-rw-r--r-- | src/parseTools.js | 9 | ||||
-rw-r--r-- | src/postamble.js | 94 | ||||
-rw-r--r-- | src/preamble.js | 85 | ||||
-rw-r--r-- | src/runtime.js | 14 | ||||
-rw-r--r-- | src/settings.js | 1687 | ||||
-rw-r--r-- | src/shell.js | 114 | ||||
-rw-r--r-- | src/utility.js | 6 |
22 files changed, 4647 insertions, 2492 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index de9a7940..1a752305 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -338,7 +338,7 @@ function analyzer(data, sidePass) { if (subItem != item && (!(subItem.intertype in UNUNFOLDABLE) || (subItem.intertype == 'value' && isNumber(subItem.ident) && isIllegalType(subItem.type)))) { if (item.intertype == 'phi') { - assert(subItem.intertype == 'value' || subItem.intertype == 'structvalue', 'We can only unfold illegal constants in phis'); + assert(subItem.intertype == 'value' || subItem.intertype == 'structvalue' || subItem.intertype in PARSABLE_LLVM_FUNCTIONS, 'We can only unfold some expressions in phis'); // we must handle this in the phi itself, if we unfold normally it will not be pushed back with the phi } else { var tempIdent = '$$etemp$' + (tempId++); @@ -502,18 +502,38 @@ function analyzer(data, sidePass) { { intertype: 'value', ident: j.toString(), type: 'i32' } ] }); - var actualSizeType = 'i' + element.bits; // The last one may be smaller than 32 bits - toAdd.push({ + var newItem = { intertype: 'load', assignTo: element.ident, - pointerType: actualSizeType + '*', - valueType: actualSizeType, - type: actualSizeType, // XXX why is this missing from intertyper? - pointer: { intertype: 'value', ident: tempVar, type: actualSizeType + '*' }, + pointerType: 'i32*', + valueType: 'i32', + type: 'i32', + pointer: { intertype: 'value', ident: tempVar, type: 'i32*' }, ident: tempVar, - pointerType: actualSizeType + '*', align: value.align - }); + }; + var newItem2 = null; + // The last one may be smaller than 32 bits + if (element.bits < 32) { + newItem.assignTo += '$preadd$'; + newItem2 = { + intertype: 'mathop', + op: 'and', + assignTo: element.ident, + type: 'i32', + params: [{ + intertype: 'value', + type: 'i32', + ident: newItem.assignTo + }, { + intertype: 'value', + type: 'i32', + ident: (0xffffffff >>> (32 - element.bits)).toString() + }], + }; + } + toAdd.push(newItem); + if (newItem2) toAdd.push(newItem2); j++; }); Types.needAnalysis['[0 x i32]'] = 0; @@ -1433,15 +1453,14 @@ function analyzer(data, sidePass) { func.labelsDict = {}; func.labelIds = {}; func.labelIdsInverse = {}; - func.labelIds[toNiceIdent('%0')] = 1; - func.labelIdsInverse[0] = toNiceIdent('%0'); - func.labelIdCounter = 2; + func.labelIdCounter = 1; func.labels.forEach(function(label) { if (!(label.ident in func.labelIds)) { func.labelIds[label.ident] = func.labelIdCounter++; func.labelIdsInverse[func.labelIdCounter-1] = label.ident; } }); + var entryIdent = func.labels[0].ident; // Minify label ids to numeric ids. func.labels.forEach(function(label) { @@ -1478,7 +1497,7 @@ function analyzer(data, sidePass) { function getActualLabelId(labelId) { if (func.labelsDict[labelId]) return labelId; // If not present, it must be a surprisingly-named entry (or undefined behavior, in which case, still ok to use the entry) - labelId = func.labelIds[ENTRY_IDENT]; + labelId = func.labelIds[entryIdent]; assert(func.labelsDict[labelId]); return labelId; } diff --git a/src/embind/embind.js b/src/embind/embind.js index 91386c69..f0cd0c74 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -5,9 +5,9 @@ /*global __emval_register, _emval_handle_array, __emval_decref*/ /*global ___getTypeName*/ /*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */ -var InternalError = Module.InternalError = extendError(Error, 'InternalError'); -var BindingError = Module.BindingError = extendError(Error, 'BindingError'); -var UnboundTypeError = Module.UnboundTypeError = extendError(BindingError, 'UnboundTypeError'); +var InternalError = Module['InternalError'] = extendError(Error, 'InternalError'); +var BindingError = Module['BindingError'] = extendError(Error, 'BindingError'); +var UnboundTypeError = Module['UnboundTypeError'] = extendError(BindingError, 'UnboundTypeError'); function throwInternalError(message) { throw new InternalError(message); @@ -638,7 +638,7 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, var tupleRegistrations = {}; -function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { +function __embind_register_value_array(rawType, name, rawConstructor, rawDestructor) { tupleRegistrations[rawType] = { name: readLatin1String(name), rawConstructor: FUNCTION_TABLE[rawConstructor], @@ -647,7 +647,7 @@ function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { }; } -function __embind_register_tuple_element( +function __embind_register_value_array_element( rawTupleType, getterReturnType, getter, @@ -666,7 +666,7 @@ function __embind_register_tuple_element( }); } -function __embind_finalize_tuple(rawTupleType) { +function __embind_finalize_value_array(rawTupleType) { var reg = tupleRegistrations[rawTupleType]; delete tupleRegistrations[rawTupleType]; var elements = reg.elements; @@ -725,7 +725,7 @@ function __embind_finalize_tuple(rawTupleType) { var structRegistrations = {}; -function __embind_register_struct( +function __embind_register_value_object( rawType, name, rawConstructor, @@ -739,7 +739,7 @@ function __embind_register_struct( }; } -function __embind_register_struct_field( +function __embind_register_value_object_field( structType, fieldName, getterReturnType, @@ -760,7 +760,7 @@ function __embind_register_struct_field( }); } -function __embind_finalize_struct(structType) { +function __embind_finalize_value_object(structType) { var reg = structRegistrations[structType]; delete structRegistrations[structType]; @@ -879,11 +879,11 @@ var genericPointerToWireType = function(destructors, handle) { if (handle.$$.smartPtrType === this) { ptr = handle.$$.smartPtr; } else { - var clonedHandle = handle.clone(); + var clonedHandle = handle['clone'](); ptr = this.rawShare( ptr, __emval_register(function() { - clonedHandle.delete(); + clonedHandle['delete'](); }) ); if (destructors !== null) { @@ -1088,7 +1088,7 @@ function getInstanceTypeName(handle) { return handle.$$.ptrType.registeredClass.name; } -ClassHandle.prototype.isAliasOf = function(other) { +ClassHandle.prototype['isAliasOf'] = function(other) { if (!(this instanceof ClassHandle)) { return false; } @@ -1118,7 +1118,7 @@ function throwInstanceAlreadyDeleted(obj) { throwBindingError(getInstanceTypeName(obj) + ' instance already deleted'); } -ClassHandle.prototype.clone = function() { +ClassHandle.prototype['clone'] = function() { if (!this.$$.ptr) { throwInstanceAlreadyDeleted(this); } diff --git a/src/embind/emval.js b/src/embind/emval.js index 77270597..0d075188 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -4,6 +4,7 @@ /*global createNamedFunction*/ /*global readLatin1String, writeStringToMemory*/ /*global requireRegisteredType, throwBindingError*/ +/*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */ var Module = Module || {}; @@ -100,7 +101,7 @@ function __emval_new_cstring(v) { function __emval_take_value(type, v) { type = requireRegisteredType(type, '_emval_take_value'); - v = type.fromWireType(v); + v = type['fromWireType'](v); return __emval_register(v); } @@ -203,7 +204,7 @@ function __emval_as(handle, returnType) { returnType = requireRegisteredType(returnType, 'emval::as'); var destructors = []; // caller owns destructing - return returnType.toWireType(destructors, _emval_handle_array[handle].value); + return returnType['toWireType'](destructors, _emval_handle_array[handle].value); } function parseParameters(argCount, argTypes, argWireTypes) { @@ -212,7 +213,7 @@ function parseParameters(argCount, argTypes, argWireTypes) { var argType = requireRegisteredType( HEAP32[(argTypes >> 2) + i], "parameter " + i); - a[i] = argType.fromWireType(argWireTypes[i]); + a[i] = argType['fromWireType'](argWireTypes[i]); } return a; } @@ -223,7 +224,7 @@ function __emval_call(handle, argCount, argTypes) { var args = new Array(argCount); for (var i = 0; i < argCount; ++i) { - args[i] = types[i].fromWireType(arguments[3 + i]); + args[i] = types[i]['fromWireType'](arguments[3 + i]); } var fn = _emval_handle_array[handle].value; @@ -247,8 +248,8 @@ function __emval_get_method_caller(argCount, argTypes) { var retType = types[0]; var signatureName = retType.name + "_$" + types.slice(1).map(function (t) { return t.name; }).join("_") + "$"; - var args1 = ["Runtime", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType"]; - var args2 = [Runtime, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType]; + var args1 = ["addFunction", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType"]; + var args2 = [Runtime.addFunction, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType]; var argsList = ""; // 'arg0, arg1, arg2, ... , argN' var argsListWired = ""; // 'arg0Wired, ..., argNWired' @@ -260,7 +261,7 @@ function __emval_get_method_caller(argCount, argTypes) { } var invokerFnBody = - "return Runtime.addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" + + "return addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" + "requireHandle(handle);\n" + "name = getStringOrSymbol(name);\n"; diff --git a/src/intertyper.js b/src/intertyper.js index abfbdacb..dd6e5522 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -700,6 +700,12 @@ function intertyper(data, sidePass, baseLineNums) { item.intertype = 'value'; if (tokensLeft[0].text == 'sideeffect') tokensLeft.splice(0, 1); item.ident = tokensLeft[0].text.substr(1, tokensLeft[0].text.length-2) || ';'; // use ; for empty inline assembly + var i = 0; + splitTokenList(tokensLeft[3].item.tokens).map(function(element) { + var ident = toNiceIdent(element[1].text); + var type = element[0].text; + item.ident = item.ident.replace(new RegExp('\\$' + i++, 'g'), ident); + }); return { forward: null, ret: [item], item: item }; } if (item.ident.substr(-2) == '()') { diff --git a/src/jsifier.js b/src/jsifier.js index 30cea99b..b377202d 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -1211,7 +1211,7 @@ function JSify(data, functionsOnly, givenFunctions) { // in an assignment var disabled = DISABLE_EXCEPTION_CATCHING == 2 && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST); var phiSets = calcPhiSets(item); - var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone); + var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone, true); var ret; @@ -1368,7 +1368,7 @@ function JSify(data, functionsOnly, givenFunctions) { return ret; }); - function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn) { + function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn, invoke) { // We cannot compile assembly. See comment in intertyper.js:'Call' assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!'); @@ -1433,7 +1433,7 @@ function JSify(data, functionsOnly, givenFunctions) { args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) }); if (ASM_JS) { - if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || funcData.setjmpTable) { + if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || funcData.setjmpTable) { args = args.map(function(arg, i) { return asmCoercion(arg, argsTypes[i]) }); } else { args = args.map(function(arg, i) { return asmEnsureFloat(arg, argsTypes[i]) }); @@ -1522,7 +1522,8 @@ function JSify(data, functionsOnly, givenFunctions) { var sig = Functions.getSignature(returnType, argsTypes, hasVarArgs); if (ASM_JS) { assert(returnType.search(/\("'\[,/) == -1); // XXX need isFunctionType(type, out) - if (!byPointerForced && !funcData.setjmpTable) { + var functionTableCall = !byPointerForced && !funcData.setjmpTable && !invoke; + if (functionTableCall) { // normal asm function pointer call callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py Functions.neededTables[sig] = 1; @@ -1537,7 +1538,7 @@ function JSify(data, functionsOnly, givenFunctions) { assert(!ASM_JS, 'cannot emit safe dyncalls in asm'); callIdent = '(tempInt=' + callIdent + ',tempInt < 0 || tempInt >= FUNCTION_TABLE.length-1 || !FUNCTION_TABLE[tempInt] ? abort("dyncall error: ' + sig + ' " + FUNCTION_TABLE_NAMES[tempInt]) : tempInt)'; } - if (!ASM_JS || (!byPointerForced && !funcData.setjmpTable)) callIdent = Functions.getTable(sig) + '[' + callIdent + ']'; + if (!ASM_JS || functionTableCall) callIdent = Functions.getTable(sig) + '[' + callIdent + ']'; } var ret = callIdent + '(' + args.join(', ') + ')'; diff --git a/src/library.js b/src/library.js index e0db3336..a6a38cfe 100644 --- a/src/library.js +++ b/src/library.js @@ -28,8 +28,9 @@ LibraryManager.library = { stderr: 'allocate(1, "i32*", ALLOC_STATIC)', _impure_ptr: 'allocate(1, "i32*", ALLOC_STATIC)', - $FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'], - $FS__postset: '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + + $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', 'stdin', 'stdout', 'stderr', 'fflush'], + $FS__postset: 'FS.staticInit();' + + '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + '__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' + '__ATEXIT__.push({ func: function() { FS.quit() } });' + // export some names through closure @@ -41,288 +42,685 @@ LibraryManager.library = { 'Module["FS_createLink"] = FS.createLink;' + 'Module["FS_createDevice"] = FS.createDevice;', $FS: { - // The path to the current folder. - currentPath: '/', - // The inode to assign to the next created object. - nextInode: 2, - // Currently opened file or directory streams. Padded with null so the zero - // index is unused, as the indices are used as pointers. This is not split - // into separate fileStreams and folderStreams lists because the pointers - // must be interchangeable, e.g. when used in fdopen(). - // streams is kept as a dense array. It may contain |null| to fill in - // holes, however. + root: null, + nodes: [null], + devices: [null], streams: [null], -#if ASSERTIONS - checkStreams: function() { - for (var i in FS.streams) if (FS.streams.hasOwnProperty(i)) assert(i >= 0 && i < FS.streams.length); // no keys not in dense span - for (var i = 0; i < FS.streams.length; i++) assert(typeof FS.streams[i] == 'object'); // no non-null holes in dense span - }, -#endif + nextInode: 1, + name_table: new Array(4096), + currentPath: '/', + initialized: false, // Whether we are currently ignoring permissions. Useful when preparing the // filesystem and creating files inside read-only folders. // This is set to false when the runtime is initialized, allowing you // to modify the filesystem freely before run() is called. ignorePermissions: true, - createFileHandle: function(stream, fd) { - if (typeof stream === 'undefined') { - stream = null; - } - if (!fd) { - if (stream && stream.socket) { - for (var i = 1; i < 64; i++) { - if (!FS.streams[i]) { - fd = i; - break; - } - } - assert(fd, 'ran out of low fds for sockets'); - } else { - fd = Math.max(FS.streams.length, 64); - for (var i = FS.streams.length; i < fd; i++) { - FS.streams[i] = null; // Keep dense - } + + ErrnoError: function(errno) { + this.errno = errno; + for (var key in ERRNO_CODES) { + if (ERRNO_CODES[key] === errno) { + this.code = key; + break; } } - // Close WebSocket first if we are about to replace the fd (i.e. dup2) - if (FS.streams[fd] && FS.streams[fd].socket && FS.streams[fd].socket.close) { - FS.streams[fd].socket.close(); + this.message = ERRNO_MESSAGES[errno]; + }, + + handleFSError: function(e) { + if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + new Error().stack; + return ___setErrNo(e.errno); + }, + + // + // nodes + // + hashName: function(parentid, name) { + var hash = 0; + for (var i = 0; i < name.length; i++) { + hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; } - FS.streams[fd] = stream; - return fd; + return (parentid + hash) % FS.name_table.length; }, - removeFileHandle: function(fd) { - FS.streams[fd] = null; + hashAddNode: function(node) { + var hash = FS.hashName(node.parent.id, node.name); + node.name_next = FS.name_table[hash]; + FS.name_table[hash] = node; }, - joinPath: function(parts, forceRelative) { - var ret = parts[0]; - for (var i = 1; i < parts.length; i++) { - if (ret[ret.length-1] != '/') ret += '/'; - ret += parts[i]; + hashRemoveNode: function(node) { + var hash = FS.hashName(node.parent.id, node.name); + if (FS.name_table[hash] === node) { + FS.name_table[hash] = node.name_next; + } else { + var current = FS.name_table[hash]; + while (current) { + if (current.name_next === node) { + current.name_next = node.name_next; + break; + } + current = current.name_next; + } } - if (forceRelative && ret[0] == '/') ret = ret.substr(1); - return ret; }, - // Converts any path to an absolute path. Resolves embedded "." and ".." - // parts. - absolutePath: function(relative, base) { - if (typeof relative !== 'string') return null; - if (base === undefined) base = FS.currentPath; - if (relative && relative[0] == '/') base = ''; - var full = base + '/' + relative; - var parts = full.split('/').reverse(); - var absolute = ['']; - while (parts.length) { - var part = parts.pop(); - if (part == '' || part == '.') { - // Nothing. - } else if (part == '..') { - if (absolute.length > 1) absolute.pop(); - } else { - absolute.push(part); + lookupNode: function(parent, name) { + var err = FS.mayLookup(parent); + if (err) { + throw new FS.ErrnoError(err); + } + var hash = FS.hashName(parent.id, name); + for (var node = FS.name_table[hash]; node; node = node.name_next) { + if (node.parent.id === parent.id && node.name === name) { + return node; } } - return absolute.length == 1 ? '/' : absolute.join('/'); + // if we failed to find it in the cache, call into the VFS + return VFS.lookup(parent, name); }, - // Analyzes a relative or absolute path returning a description, containing: - // isRoot: Whether the path points to the root. - // exists: Whether the object at the path exists. - // error: If !exists, this will contain the errno code of the cause. - // name: The base name of the object (null if !parentExists). - // path: The absolute path to the object, with all links resolved. - // object: The filesystem record of the object referenced by the path. - // parentExists: Whether the parent of the object exist and is a folder. - // parentPath: The absolute path to the parent folder. - // parentObject: The filesystem record of the parent folder. - analyzePath: function(path, dontResolveLastLink, linksVisited) { - var ret = { - isRoot: false, - exists: false, - error: 0, - name: null, - path: null, - object: null, - parentExists: false, - parentPath: null, - parentObject: null + createNode: function(parent, name, mode, rdev) { + var node = { + id: FS.nextInode++, + name: name, + mode: mode, + node_ops: {}, + stream_ops: {}, + rdev: rdev, + parent: null, + mount: null }; -#if FS_LOG - var inputPath = path; - function log() { - Module['print']('FS.analyzePath("' + inputPath + '", ' + - dontResolveLastLink + ', ' + - linksVisited + ') => {' + - 'isRoot: ' + ret.isRoot + ', ' + - 'exists: ' + ret.exists + ', ' + - 'error: ' + ret.error + ', ' + - 'name: "' + ret.name + '", ' + - 'path: "' + ret.path + '", ' + - 'object: ' + ret.object + ', ' + - 'parentExists: ' + ret.parentExists + ', ' + - 'parentPath: "' + ret.parentPath + '", ' + - 'parentObject: ' + ret.parentObject + '}'); + if (!parent) { + parent = node; // root node sets parent to itself + } + node.parent = parent; + node.mount = parent.mount; + // compatibility + var readMode = {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}}; + var writeMode = {{{ cDefine('S_IWUGO') }}}; + Object.defineProperty(node, 'read', { + get: function() { return (node.mode & readMode) === readMode; }, + set: function(val) { val ? node.mode |= readMode : node.mode &= ~readMode; } + }); + Object.defineProperty(node, 'write', { + get: function() { return (node.mode & writeMode) === writeMode; }, + set: function(val) { val ? node.mode |= writeMode : node.mode &= ~writeMode; } + }); + // TODO add: + // isFolder + // isDevice + FS.hashAddNode(node); + return node; + }, + destroyNode: function(node) { + FS.hashRemoveNode(node); + }, + isRoot: function(node) { + return node === node.parent; + }, + isMountpoint: function(node) { + return node.mounted; + }, + isFile: function(mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFREG') }}}; + }, + isDir: function(mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFDIR') }}}; + }, + isLink: function(mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFLNK') }}}; + }, + isChrdev: function(mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFCHR') }}}; + }, + isBlkdev: function(mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFBLK') }}}; + }, + isFIFO: function(mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFIFO') }}}; + }, + + // + // paths + // + cwd: function() { + return FS.currentPath; + }, + lookupPath: function(path, opts) { + path = PATH.resolve(FS.currentPath, path); + opts = opts || { recurse_count: 0 }; + + if (opts.recurse_count > 8) { // max recursive lookup of 8 + throw new FS.ErrnoError(ERRNO_CODES.ELOOP); } -#endif - path = FS.absolutePath(path); - if (path == '/') { - ret.isRoot = true; - ret.exists = ret.parentExists = true; - ret.name = '/'; - ret.path = ret.parentPath = '/'; - ret.object = ret.parentObject = FS.root; - } else if (path !== null) { - linksVisited = linksVisited || 0; - path = path.slice(1).split('/'); - var current = FS.root; - var traversed = ['']; - while (path.length) { - if (path.length == 1 && current.isFolder) { - ret.parentExists = true; - ret.parentPath = traversed.length == 1 ? '/' : traversed.join('/'); - ret.parentObject = current; - ret.name = path[0]; - } - var target = path.shift(); - if (!current.isFolder) { - ret.error = ERRNO_CODES.ENOTDIR; - break; - } else if (!current.read) { - ret.error = ERRNO_CODES.EACCES; - break; - } else if (!current.contents.hasOwnProperty(target)) { - ret.error = ERRNO_CODES.ENOENT; - break; - } - current = current.contents[target]; - if (current.link && !(dontResolveLastLink && path.length == 0)) { - if (linksVisited > 40) { // Usual Linux SYMLOOP_MAX. - ret.error = ERRNO_CODES.ELOOP; - break; + + // split the path + var parts = PATH.normalizeArray(path.split('/').filter(function(p) { + return !!p; + }), false); + + // start at the root + var current = FS.root; + var current_path = '/'; + + for (var i = 0; i < parts.length; i++) { + var islast = (i === parts.length-1); + if (islast && opts.parent) { + // stop resolving + break; + } + + current = FS.lookupNode(current, parts[i]); + current_path = PATH.join(current_path, parts[i]); + + // jump to the mount's root node if this is a mountpoint + if (FS.isMountpoint(current)) { + current = current.mount.root; + } + + // follow symlinks + // by default, lookupPath will not follow a symlink if it is the final path component. + // setting opts.follow = true will override this behavior. + if (!islast || opts.follow) { + var count = 0; + while (FS.isLink(current.mode)) { + var link = VFS.readlink(current_path); + current_path = PATH.resolve(PATH.dirname(current_path), link); + + var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count }); + current = lookup.node; + + if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX). + throw new FS.ErrnoError(ERRNO_CODES.ELOOP); } - var link = FS.absolutePath(current.link, traversed.join('/')); - ret = FS.analyzePath([link].concat(path).join('/'), - dontResolveLastLink, linksVisited + 1); -#if FS_LOG - log(); -#endif - return ret; - } - traversed.push(target); - if (path.length == 0) { - ret.exists = true; - ret.path = traversed.join('/'); - ret.object = current; } } } -#if FS_LOG - log(); -#endif - return ret; + + return { path: current_path, node: current }; }, - // Finds the file system object at a given path. If dontResolveLastLink is - // set to true and the object is a symbolic link, it will be returned as is - // instead of being resolved. Links embedded in the path are still resolved. - findObject: function(path, dontResolveLastLink) { - FS.ensureRoot(); - var ret = FS.analyzePath(path, dontResolveLastLink); - if (ret.exists) { - return ret.object; + getPath: function(node) { + var path; + while (true) { + if (FS.isRoot(node)) { + return path ? PATH.join(node.mount.mountpoint, path) : node.mount.mountpoint; + } + path = path ? PATH.join(node.name, path) : node.name; + node = node.parent; + } + }, + + // + // permissions + // + flagModes: { + '"r"': {{{ cDefine('O_RDONLY') }}}, + '"rs"': {{{ cDefine('O_RDONLY') }}} | {{{ cDefine('O_SYNC') }}}, + '"r+"': {{{ cDefine('O_RDWR') }}}, + '"w"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}}, + '"wx"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, + '"xw"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, + '"w+"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}}, + '"wx+"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}}, + '"xw+"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}}, + '"a"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}}, + '"ax"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, + '"xa"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, + '"a+"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}}, + '"ax+"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}}, + '"xa+"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}} + }, + // convert the 'r', 'r+', etc. to it's corresponding set of O_* flags + modeStringToFlags: function(str) { + var flags = FS.flagModes[str]; + if (typeof flags === 'undefined') { + throw new Error('Unknown file open mode: ' + str); + } + return flags; + }, + // convert O_* bitmask to a string for nodePermissions + flagsToPermissionString: function(flag) { + var accmode = flag &am |