aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js45
-rw-r--r--src/embind/embind.js26
-rw-r--r--src/embind/emval.js15
-rw-r--r--src/intertyper.js6
-rw-r--r--src/jsifier.js11
-rw-r--r--src/library.js3968
-rw-r--r--src/library_browser.js31
-rw-r--r--src/library_egl.js9
-rw-r--r--src/library_gl.js17
-rw-r--r--src/library_glut.js2
-rw-r--r--src/library_jansson.js2
-rw-r--r--src/library_openal.js596
-rw-r--r--src/library_path.js134
-rw-r--r--src/library_sdl.js266
-rw-r--r--src/modules.js2
-rw-r--r--src/parseTools.js9
-rw-r--r--src/postamble.js94
-rw-r--r--src/preamble.js85
-rw-r--r--src/runtime.js14
-rw-r--r--src/settings.js1687
-rw-r--r--src/shell.js114
-rw-r--r--src/utility.js6
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