mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
415 lines
12 KiB
JavaScript
415 lines
12 KiB
JavaScript
/*
|
|
* Executable.js
|
|
* Objective-J
|
|
*
|
|
* Created by Francisco Tolmasky.
|
|
* Copyright 2010, 280 North, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
var ExecutableUnloadedFileDependencies = 0,
|
|
ExecutableLoadingFileDependencies = 1,
|
|
ExecutableLoadedFileDependencies = 2;
|
|
|
|
function Executable(/*String*/ aCode, /*Array*/ fileDependencies, /*CFURL*/ aURL, /*Function*/ aFunction)
|
|
{
|
|
if (arguments.length === 0)
|
|
return this;
|
|
|
|
this._code = aCode;
|
|
this._function = aFunction || NULL;
|
|
this._URL = aURL || new CFURL("(Anonymous)", mainBundleURL);
|
|
|
|
this._fileDependencies = fileDependencies;
|
|
this._fileDependencyLoadStatus = ExecutableUnloadedFileDependencies;
|
|
|
|
this._eventDispatcher = new EventDispatcher(this);
|
|
|
|
if (this._function)
|
|
return;
|
|
|
|
this.setCode(aCode);
|
|
}
|
|
|
|
exports.Executable = Executable;
|
|
|
|
Executable.prototype.path = function()
|
|
{
|
|
return this.URL().path();
|
|
}
|
|
|
|
Executable.prototype.URL = function()
|
|
{
|
|
return this._URL;
|
|
}
|
|
|
|
Executable.prototype.functionParameters = function()
|
|
{
|
|
var functionParameters = ["global", "objj_executeFile", "objj_importFile"];
|
|
|
|
//exportedNames().concat("objj_executeFile", "objj_importFile");
|
|
|
|
#ifdef COMMONJS
|
|
functionParameters = functionParameters.concat("require", "exports", "module", "system", "print", "window");
|
|
#endif
|
|
|
|
return functionParameters;
|
|
}
|
|
|
|
Executable.prototype.functionArguments = function()
|
|
{
|
|
var functionArguments = [global, this.fileExecuter(), this.fileImporter()];
|
|
|
|
#ifdef COMMONJS
|
|
functionArguments = functionArguments.concat(Executable.commonJSArguments());
|
|
#endif
|
|
|
|
return functionArguments;
|
|
}
|
|
|
|
#ifdef COMMONJS
|
|
Executable.setCommonJSParameters = function()
|
|
{
|
|
this._commonJSParameters = Array.prototype.slice.call(arguments);
|
|
}
|
|
|
|
Executable.commonJSParameters = function()
|
|
{
|
|
return this._commonJSParameters || [];
|
|
}
|
|
|
|
Executable.setCommonJSArguments = function()
|
|
{
|
|
this._commonJSArguments = Array.prototype.slice.call(arguments);
|
|
}
|
|
|
|
Executable.commonJSArguments = function()
|
|
{
|
|
return this._commonJSArguments || [];
|
|
}
|
|
|
|
Executable.prototype.toMarkedString = function()
|
|
{
|
|
var markedString = "@STATIC;1.0;",
|
|
dependencies = this.fileDependencies(),
|
|
index = 0,
|
|
count = dependencies.length;
|
|
|
|
for (; index < count; ++index)
|
|
markedString += dependencies[index].toMarkedString();
|
|
|
|
var code = this.code();
|
|
|
|
return markedString + MARKER_TEXT + ";" + code.length + ";" + code;
|
|
}
|
|
#endif
|
|
|
|
Executable.prototype.execute = function()
|
|
{
|
|
#if EXECUTION_LOGGING
|
|
CPLog("EXECUTION: " + this.URL());
|
|
#endif
|
|
var oldContextBundle = CONTEXT_BUNDLE;
|
|
|
|
CONTEXT_BUNDLE = CFBundle.bundleContainingPath(this.path());
|
|
|
|
var result = this._function.apply(global, this.functionArguments());
|
|
|
|
CONTEXT_BUNDLE = oldContextBundle;
|
|
|
|
return result;
|
|
}
|
|
|
|
Executable.prototype.code = function()
|
|
{
|
|
return this._code;
|
|
}
|
|
|
|
Executable.prototype.setCode = function(code)
|
|
{
|
|
this._code = code;
|
|
|
|
var parameters = this.functionParameters().join(","),
|
|
absoluteString = this.URL().absoluteString();
|
|
|
|
#if COMMONJS
|
|
if (typeof system !== "undefined" && system.engine === "rhino")
|
|
{
|
|
code = "function(" + parameters + "){" + code + "/**/\n}";
|
|
this._function = Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(window, code, absoluteString, 0, NULL);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
// "//@ sourceURL=" at the end lets us name our eval'd files for debuggers, etc.
|
|
// * WebKit: http://pmuellr.blogspot.com/2009/06/debugger-friendly.html
|
|
// * Firebug: http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/
|
|
//if (YES) {
|
|
code += "/**/\n//@ sourceURL=" + "hello" + absoluteString;
|
|
this._function = new Function(parameters, code);
|
|
//} else {
|
|
// // Firebug only does it for "eval()", not "new Function()". Ugh. Slower.
|
|
// var functionText = "(function(){"+GET_CODE(aFragment)+"/**/\n})\n//@ sourceURL="+GET_FILE(aFragment).path;
|
|
// compiled = eval(functionText);
|
|
//}
|
|
this._function.displayName = absoluteString;
|
|
#if COMMONJS
|
|
}
|
|
#endif
|
|
}
|
|
|
|
Executable.prototype.fileDependencies = function()
|
|
{
|
|
return this._fileDependencies;
|
|
}
|
|
|
|
Executable.prototype.scope = function()
|
|
{
|
|
return this._scope;
|
|
}
|
|
|
|
Executable.prototype.hasLoadedFileDependencies = function()
|
|
{
|
|
return this._fileDependencyLoadStatus === ExecutableLoadedFileDependencies;
|
|
}
|
|
var globalIteration = 0;
|
|
|
|
Executable.prototype.loadFileDependencies = function()
|
|
{
|
|
#if DEPENDENCY_LOGGING
|
|
CPLog("DEPENDENCY: initiated by " + this.scope());
|
|
#endif
|
|
if (this._fileDependencyLoadStatus !== ExecutableUnloadedFileDependencies)
|
|
return;
|
|
|
|
this._fileDependencyLoadStatus = ExecutableLoadingFileDependencies;
|
|
|
|
var searchedPaths = [{ }, { }],
|
|
fileExecutableSearches = new CFMutableDictionary(),
|
|
incompleteFileExecutableSearches = new CFMutableDictionary(),
|
|
loadingExecutables = { };
|
|
|
|
function searchForFileDependencies(/*Executable*/ anExecutable)
|
|
{
|
|
var executables = [anExecutable],
|
|
executableIndex = 0,
|
|
executableCount = executables.length;
|
|
|
|
for (; executableIndex < executableCount; ++executableIndex)
|
|
{
|
|
var executable = executables[executableIndex];
|
|
|
|
if (executable.hasLoadedFileDependencies())
|
|
continue;
|
|
|
|
var executablePath = executable.path();
|
|
|
|
loadingExecutables[executablePath] = executable;
|
|
|
|
var cwd = FILE.dirname(executablePath),
|
|
fileDependencies = executable.fileDependencies(),
|
|
fileDependencyIndex = 0,
|
|
fileDependencyCount = fileDependencies.length;
|
|
|
|
for (; fileDependencyIndex < fileDependencyCount; ++fileDependencyIndex)
|
|
{
|
|
var fileDependency = fileDependencies[fileDependencyIndex],
|
|
isLocal = fileDependency.isLocal(),
|
|
path = importablePath(fileDependency.path(), isLocal, cwd);
|
|
|
|
if (searchedPaths[isLocal ? 1 : 0][path])
|
|
continue;
|
|
|
|
searchedPaths[isLocal ? 1 : 0][path] = YES;
|
|
|
|
var fileExecutableSearch = new FileExecutableSearch(path, isLocal),
|
|
fileExecutableSearchUID = fileExecutableSearch.UID();
|
|
|
|
if (fileExecutableSearches.containsKey(fileExecutableSearchUID))
|
|
continue;
|
|
|
|
fileExecutableSearches.setValueForKey(fileExecutableSearchUID, fileExecutableSearch);
|
|
|
|
if (fileExecutableSearch.isComplete())
|
|
{
|
|
executables.push(fileExecutableSearch.result());
|
|
++executableCount;
|
|
}
|
|
|
|
else
|
|
{
|
|
incompleteFileExecutableSearches.setValueForKey(fileExecutableSearchUID, fileExecutableSearch);
|
|
|
|
fileExecutableSearch.addEventListener("complete", function( anEvent)
|
|
{
|
|
var fileExecutableSearch = anEvent.fileExecutableSearch;
|
|
|
|
incompleteFileExecutableSearches.removeValueForKey(fileExecutableSearch.UID());
|
|
|
|
searchForFileDependencies(fileExecutableSearch.result());
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (incompleteFileExecutableSearches.count() > 0)
|
|
#if !DEPENDENCY_LOGGING
|
|
return;
|
|
#else
|
|
{
|
|
CPLog("DEPENDENCY: more dependencies: ");
|
|
CPLog(incompleteFileExecutableSearches.toString());
|
|
return;
|
|
}
|
|
|
|
CPLog("DEPENDENCY: Ended");
|
|
#endif
|
|
|
|
for (var path in loadingExecutables)
|
|
if (hasOwnProperty.call(loadingExecutables, path))
|
|
loadingExecutables[path]._fileDependencyLoadStatus = ExecutableLoadedFileDependencies;
|
|
|
|
for (var path in loadingExecutables)
|
|
if (hasOwnProperty.call(loadingExecutables, path))
|
|
{
|
|
var executable = loadingExecutables[path];
|
|
|
|
executable._eventDispatcher.dispatchEvent(
|
|
{
|
|
type:"dependenciesload",
|
|
executable:executable
|
|
});
|
|
}
|
|
}
|
|
|
|
searchForFileDependencies(this);
|
|
}
|
|
|
|
Executable.prototype.addEventListener = function(/*String*/ anEventName, /*Function*/ aListener)
|
|
{
|
|
this._eventDispatcher.addEventListener(anEventName, aListener);
|
|
}
|
|
|
|
Executable.prototype.removeEventListener = function(/*String*/ anEventName, /*Function*/ aListener)
|
|
{
|
|
this._eventDispatcher.removeEventListener(anEventName, aListener);
|
|
}
|
|
|
|
function importablePath(/*String*/ aPath, /*BOOL*/ isLocal, /*String*/ aCWD)
|
|
{
|
|
aPath = FILE.normal(aPath);
|
|
|
|
if (FILE.isAbsolute(aPath))
|
|
return aPath;
|
|
|
|
if (isLocal)
|
|
aPath = FILE.normal(FILE.join(aCWD, aPath));
|
|
|
|
return aPath;
|
|
}
|
|
|
|
Executable.prototype.fileImporter = function()
|
|
{
|
|
return Executable.fileImporterForPath(FILE.dirname(this.path()));
|
|
}
|
|
|
|
Executable.prototype.fileExecuter = function()
|
|
{
|
|
return Executable.fileExecuterForPath(FILE.dirname(this.path()));
|
|
}
|
|
|
|
var cachedFileExecutersForPaths = { };
|
|
|
|
Executable.fileExecuterForPath = function(/*String*/ referencePath)
|
|
{
|
|
referencePath = FILE.normal(referencePath);
|
|
|
|
var fileExecuter = cachedFileExecutersForPaths[referencePath];
|
|
|
|
if (!fileExecuter)
|
|
{
|
|
fileExecuter = function(/*String*/ aPath, /*BOOL*/ isLocal, /*BOOL*/ shouldForce)
|
|
{
|
|
aPath = importablePath(aPath, isLocal, referencePath);
|
|
|
|
var fileExecutableSearch = new FileExecutableSearch(aPath, isLocal),
|
|
fileExecutable = fileExecutableSearch.result();
|
|
|
|
if (0 && !fileExecutable.hasLoadedFileDependencies())
|
|
throw "No executable loaded for file at path " + aPath;
|
|
|
|
fileExecutable.execute(shouldForce);
|
|
}
|
|
|
|
cachedFileExecutersForPaths[referencePath] = fileExecuter;
|
|
}
|
|
|
|
return fileExecuter;
|
|
}
|
|
|
|
var cachedImportersForPaths = { };
|
|
|
|
Executable.fileImporterForPath = function(/*String*/ referencePath)
|
|
{
|
|
referencePath = FILE.normal(referencePath);
|
|
|
|
var cachedImporter = cachedImportersForPaths[referencePath];
|
|
|
|
if (!cachedImporter)
|
|
{
|
|
cachedImporter = function(/*String*/ aPath, /*BOOL*/ isLocal, /*Function*/ aCallback)
|
|
{
|
|
aPath = importablePath(aPath, isLocal, referencePath);
|
|
|
|
var fileExecutableSearch = new FileExecutableSearch(aPath, isLocal);
|
|
|
|
function searchComplete(/*FileExecutableSearch*/ aFileExecutableSearch)
|
|
{
|
|
var fileExecutable = aFileExecutableSearch.result(),
|
|
fileExecuter = Executable.fileExecuterForPath(referencePath),
|
|
executeAndCallback = function ()
|
|
{
|
|
fileExecuter(aPath, isLocal);
|
|
|
|
if (aCallback)
|
|
aCallback();
|
|
}
|
|
|
|
if (!fileExecutable.hasLoadedFileDependencies())
|
|
{
|
|
fileExecutable.addEventListener("dependenciesload", executeAndCallback);
|
|
fileExecutable.loadFileDependencies();
|
|
}
|
|
else
|
|
executeAndCallback();
|
|
}
|
|
|
|
if (fileExecutableSearch.isComplete())
|
|
searchComplete(fileExecutableSearch);
|
|
else
|
|
fileExecutableSearch.addEventListener("complete", function(/*Event*/ anEvent)
|
|
{
|
|
searchComplete(anEvent.fileExecutableSearch);
|
|
});
|
|
}
|
|
|
|
cachedImportersForPaths[referencePath] = cachedImporter;
|
|
}
|
|
|
|
return cachedImporter;
|
|
}
|
|
|