Skip to content

Commit

Permalink
Implements #165
Browse files Browse the repository at this point in the history
  • Loading branch information
iccir committed Mar 5, 2023
1 parent a466cc6 commit a51b091
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 158 deletions.
3 changes: 0 additions & 3 deletions lib/runtime.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,7 @@ declare class N$_BaseClass implements N$_BaseProtocol {
static class() : typeof N$_BaseClass;
static respondsToSelector_(aSelector : N$_Selector) : boolean;
static instancesRespondToSelector_(aSelector : N$_Selector) : boolean;
static isKindOfClass_(cls : typeof N$_BaseClass) : boolean;
static isMemberOfClass_(cls : typeof N$_BaseClass) : boolean;
static isSubclassOfClass_(cls : typeof N$_BaseClass) : boolean;
static isEqual_(other : N$_BaseClass) : boolean;

init() : N$_BaseClass;
copy() : any;
Expand Down
139 changes: 30 additions & 109 deletions lib/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,37 @@ var root = this;
var previousNilscript = root.nilscript;

var _nameKey = "N$_name";
var _superKey = "N$_super";
var _baseClassKey = "N$_base";
var _globalKey = "N$$_";

var BaseObject = function BaseObject() { }
class NSObject {

var _classSymbolToReadyArrayMap = { };
var _classSymbolToClassMap = { "N$_base": BaseObject };
static alloc() { return new this(); }
static class() { return this; }
static superclass() { return class_getSuperclass(this); }
static className() { return class_getName(this); }
static respondsToSelector_(aSelector) { return !!this[aSelector]; }
static instancesRespondToSelector_(aSelector) { return class_respondsToSelector(this, aSelector); }
static isSubclassOfClass_(cls) { return class_isSubclassOf(this, cls); }

init() { return this; }
copy() { return object_getClass(this).alloc().init(); }
superclass() { return class_getSuperclass(object_getClass(this)); }
class() { return object_getClass(this); }
className() { return class_getName(object_getClass(this)); }
respondsToSelector_(aSelector) { return class_respondsToSelector(object_getClass(this), aSelector); }
performSelector_(aSelector) { return msgSend(this, aSelector); }
performSelector_withObject_(aSelector, object) { return msgSend(this, aSelector, object); }
performSelector_withObject_withObject_(aSelector, o1, o2) { return msgSend(this, aSelector, o1, o2); }
description() { return "<" + this.className() + " " + this["N$_id"] + ">" }
toString() { return this.description(); }
isKindOfClass_(cls) { return class_isSubclassOf(object_getClass(this), cls); }
isMemberOfClass_(cls) { return object_getClass(this) === cls; }
isEqual_(other) { return this === other; }

}

var _classSymbolToClassMap = { "N$_base": NSObject }


function _reset()
Expand All @@ -29,10 +52,9 @@ function _reset()
}
}

clear(_classSymbolToReadyArrayMap);
clear(_classSymbolToClassMap);

_classSymbolToClassMap[_baseClassKey] = BaseObject;
_classSymbolToClassMap[_baseClassKey] = NSObject;
}


Expand All @@ -56,16 +78,6 @@ function cloneJSObject(object)
}


function mixin(from, to, overwrite, callback)
{
for (var key in from) { if (hop(from, key) && (overwrite || !hop(to, key))) {
var value = from[key];
if (callback) callback(key, value);
to[key] = value;
}}
}


function _getMethodDisplayName(className, methodName, prefix)
{
if (className.indexOf("N$_") == 0) {
Expand All @@ -81,76 +93,12 @@ function _getMethodDisplayName(className, methodName, prefix)
}


function _callWhenClassReady(name, f)
{
if (_classSymbolToClassMap[name]) {
f();

} else {
var readyArray = _classSymbolToReadyArrayMap[name];

if (readyArray) {
readyArray.push(f);
} else {
_classSymbolToReadyArrayMap[name] = [ f ];
}
}
}


function throwUnrecognizedSelector(receiver, selector)
{
throw new Error("Unrecognized selector: " + sel_getName(selector) + " sent to instance " + receiver);
}


function _registerClass(classSymbol, superSymbol, callback)
{
var isSubclassOfBase = false;

if (!superSymbol) {
superSymbol = _baseClassKey;
isSubclassOfBase = true;
}

var cls;

_callWhenClassReady(superSymbol, function() {
var superclass = isSubclassOfBase ? BaseObject : _classSymbolToClassMap[superSymbol];
if (!superclass) return;

var instance_methods = { };
var class_methods = { };

cls = callback(class_methods, instance_methods);

cls.displayName = _getReadableForSymbol(classSymbol);
cls[_nameKey] = classSymbol;
cls[_superKey] = superclass;
cls.prototype = new superclass();

mixin(superclass, cls);

mixin(class_methods, cls, true, function(key, method) {
method.displayName = _getMethodDisplayName(classSymbol, key, "+");
});

mixin(instance_methods, cls.prototype, true, function(key, method) {
method.displayName = _getMethodDisplayName(classSymbol, key, "-");
});

_classSymbolToClassMap[classSymbol] = cls;

var readyArray = _classSymbolToReadyArrayMap[classSymbol];
if (readyArray) {
for (var i = 0, length = readyArray.length; i < length; i++) {
readyArray[i]();
}
}
});
}


function noConflict()
{
root.nilscript = previousNilscript;
Expand Down Expand Up @@ -191,7 +139,7 @@ function getClassList()

function isObject(object)
{
return !!(object && object.constructor[_nameKey]);
return object instanceof NSObject;
}


Expand Down Expand Up @@ -229,7 +177,7 @@ function class_getName(cls)

function class_getSuperclass(cls)
{
return cls[_superKey];
return Object.getPrototypeOf(cls);
}


Expand Down Expand Up @@ -267,7 +215,6 @@ function msgSend(receiver, selector)

var nilscript = {
_id: 0,
_registerClass: _registerClass,
_c: _classSymbolToClassMap,
_g: { },
_reset: _reset,
Expand All @@ -290,32 +237,6 @@ var nilscript = {
};


BaseObject.alloc = function() { return new this(); }
BaseObject["class"] = function() { return this; }
BaseObject.superclass = function() { return class_getSuperclass(this); }
BaseObject.className = function() { return class_getName(this); }
BaseObject.respondsToSelector_ = function(aSelector) { return !!this[aSelector]; }
BaseObject.instancesRespondToSelector_ = function(aSelector) { return class_respondsToSelector(this, aSelector); }
BaseObject.isKindOfClass_ = function(cls) { return class_isSubclassOf(this, cls); }
BaseObject.isMemberOfClass_ = function(cls) { return this === cls; }
BaseObject.isSubclassOfClass_ = function(cls) { return class_isSubclassOf(this, cls); }
BaseObject.isEqual_ = function(other) { return this === other; }

BaseObject.prototype.init = function() { return this; }
BaseObject.prototype.copy = function() { return object_getClass(this).alloc().init(); }
BaseObject.prototype.superclass = function() { return class_getSuperclass(object_getClass(this)); }
BaseObject.prototype["class"] = function() { return object_getClass(this); }
BaseObject.prototype.className = function() { return class_getName(object_getClass(this)); }
BaseObject.prototype.respondsToSelector_ = function(aSelector) { return class_respondsToSelector(object_getClass(this), aSelector); }
BaseObject.prototype.performSelector_ = function(aSelector) { return msgSend(this, aSelector); }
BaseObject.prototype.performSelector_withObject_ = function(aSelector, object) { return msgSend(this, aSelector, object); }
BaseObject.prototype.performSelector_withObject_withObject_ = function(aSelector, o1, o2) { return msgSend(this, aSelector, o1, o2); }
BaseObject.prototype.description = function() { return "<" + this.className() + " " + this["N$_id"] + ">" }
BaseObject.prototype.toString = function() { return this.description(); }
BaseObject.prototype.isKindOfClass_ = function(cls) { return class_isSubclassOf(object_getClass(this), cls); }
BaseObject.prototype.isMemberOfClass_ = function(cls) { return object_getClass(this) === cls; }
BaseObject.prototype.isEqual_ = function(other) { return this === other; }

if (typeof module != "undefined" && typeof module != "function") {
module.exports = nilscript;

Expand Down
49 changes: 49 additions & 0 deletions src/Compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,52 @@ async _buildFiles(files, model, options)
}


async _reorderFiles(inOutFiles, model, options)
{
let files = inOutFiles.slice(0);
inOutFiles.splice(0, inOutFiles.length);

let remainingFiles = { };
_.each(files, file => (remainingFiles[file.path] = file));

let dependencies = { };

_.each(model.classes, nsClass => {
let classPath = nsClass.location.path;
let superPath = nsClass.superclass?.location?.path;

if (superPath && (superPath != classPath)) {
let arr = dependencies[classPath] || [ ];
arr.push(superPath);
dependencies[classPath] = arr;
}
});

function addFile(path, stack) {
if (stack.includes(path)) {
stack.push(path);
throw new Error("Recursive class dependency detected: " + stack.join(","));
}

stack = [...stack, path];

_.each(dependencies[path], dependency => {
addFile(dependency, stack);
});

let file = remainingFiles[path];
if (!file) return;

remainingFiles[path] = null;
inOutFiles.push(file);
}

_.each(files, file => {
addFile(file.path, [ ]);
});
}


async _generateJavaScript(files, model, options)
{
let afterCompileCallback = options["after-compile"];
Expand Down Expand Up @@ -529,6 +575,9 @@ async compile(options)
// Build model
await this._buildFiles(files, model, options);

// Reorder files
await this._reorderFiles(files, model, options);

// Perform model diff
if (!previousModel || previousModel.hasGlobalChanges(model)) {
if (!previousModel) {
Expand Down
Loading

0 comments on commit a51b091

Please sign in to comment.