From 9b99457871fecc86bd500e83b54d7fa82d56e025 Mon Sep 17 00:00:00 2001 From: tlrobinson Date: Mon, 20 Jul 2009 18:16:17 -0700 Subject: [PATCH] Preliminary typechecking version of objj_msgSend, and preprocessor support. --- Objective-J/preprocess.js | 23 +++++++++-- Objective-J/runtime.js | 81 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/Objective-J/preprocess.js b/Objective-J/preprocess.js index 0032ed203..f09af79f1 100644 --- a/Objective-J/preprocess.js +++ b/Objective-J/preprocess.js @@ -578,12 +578,15 @@ objj_preprocessor.prototype.method = function(tokens) var buffer = new objj_stringBuffer(), token, selector = "", - parameters = []; + parameters = [], + types = [null]; while((token = tokens.skip_whitespace()) && token != TOKEN_OPEN_BRACE) { if (token == TOKEN_COLON) { + var type = ""; + // Colons are part of the selector name selector += token; @@ -592,18 +595,30 @@ objj_preprocessor.prototype.method = function(tokens) if (token == TOKEN_OPEN_PARENTHESIS) { // Swallow parameter/return type. Perhaps later we can use this for debugging? - while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS) ; + while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS) + type += token; token = tokens.skip_whitespace(); } + // Add the type. If it's empty, add null instead. + types[parameters.length+1] = type || null; + // Since this follows a colon, this must be the parameter name. parameters[parameters.length] = token; } else if (token == TOKEN_OPEN_PARENTHESIS) + { + var type = ""; + // Since :( is handled above, this must be the return type, just swallow it. - while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS) ; + while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS) + type += token; + + // types[0] is the return argument + types[0] = type || null; + } // Argument list ", ..." else if (token == TOKEN_COMMA) @@ -640,7 +655,7 @@ objj_preprocessor.prototype.method = function(tokens) CONCAT(buffer, ")\n{ with(self)\n{"); CONCAT(buffer, this.preprocess(tokens, NULL, TOKEN_CLOSE_BRACE, TOKEN_OPEN_BRACE)); - CONCAT(buffer, "}\n})"); + CONCAT(buffer, "}\n},"+types.toSource()+")"); return buffer; } diff --git a/Objective-J/runtime.js b/Objective-J/runtime.js index 365ab725a..fa6fb2273 100644 --- a/Objective-J/runtime.js +++ b/Objective-J/runtime.js @@ -542,3 +542,84 @@ function sel_registerName(aName) { return aName; } + + +// Type checking version of objj_msgSend: + +function objj_msgSendTypeCheck(/*id*/ aReceiver, /*SEL*/ aSelector) +{ + if (aReceiver == nil) + return nil; + + CLASS_GET_METHOD_IMPLEMENTATION(var implementation, aReceiver.isa, aSelector); + + var types = method.types; + + for (var i = 2; i < arguments.length; i++) + { + try + { + runtimeTypeCheck(types[i-1], arguments[i]); + } + catch (e) + { + objj_fprintf(warning_stream, "Type check failure: "+ + "aReceiver="+String(aReceiver).substring(0,100)+", "+ + "aSelector="+String(aSelector).substring(0,100)+", "+ + "argument #"+(i-2)+": " + e); + } + } + + var result = implementation.apply(aReceiver, arguments); + + try + { + runtimeTypeCheck(types[0], result); + } + catch (e) + { + objj_fprintf(warning_stream, "Type check failure: "+ + "aReceiver="+String(aReceiver).substring(0,100)+", "+ + "aSelector="+String(aSelector).substring(0,100)+", "+ + "return value: " + e); + } + + return result; +} + +function runtimeTypeCheck(typeName, actual) +{ + var objjClass; + + if (!typeName) + { + return; + } + if (typeName === "id") + { + return; + } + else if (typeName === "void") + { + if (actual === undefined) + return; + } + else if (objjClass = objj_getClass(typeName)) + { + if (actual && actual.isa) + { + var theClass = actual.isa; + for(; theClass; theClass = theClass.super_class) + if (theClass === objjClass) + return; + } + } + else + { + return; + } + + throw("Expected " + typeName + ", was [" + actual +"]"); +} + +objj_msgSend = objj_msgSendTypeCheck;