Worked on kotlinJsLib and supported trait extending other trait.

This commit is contained in:
Pavel Talanov
2011-11-16 19:37:03 +04:00
parent eec6fd137f
commit 81d7f86b93
7 changed files with 453 additions and 29 deletions
@@ -31,34 +31,20 @@ public final class ClassTranslator extends AbstractTranslator {
}
@NotNull
public JsStatement translateClass(@NotNull JetClass classDeclaration) {
if (!classDeclaration.isTrait()) {
return translateAsClassWithState(classDeclaration);
} else {
return translateAsStatelessTrait(classDeclaration);
}
}
@NotNull
private JsStatement translateAsStatelessTrait(@NotNull JetClass classDeclaration) {
JsObjectLiteral traitLiteral = translateClassDeclarations(classDeclaration);
return AstUtil.convertToStatement
(AstUtil.newAssignment(namespaceQualifiedClassNameReference(classDeclaration), traitLiteral));
}
@NotNull
private JsStatement translateAsClassWithState(@NotNull JetClass jetClassDeclaration) {
JsInvocation jsClassDeclaration = createMethodInvocation();
public JsStatement translateClass(@NotNull JetClass jetClassDeclaration) {
JsInvocation jsClassDeclaration = classCreateMethodInvocation(jetClassDeclaration);
addSuperclassReferences(jetClassDeclaration, jsClassDeclaration);
addClassOwnDeclarations(jetClassDeclaration, jsClassDeclaration);
return classDeclarationStatement(jetClassDeclaration, jsClassDeclaration);
}
@NotNull
private JsInvocation createMethodInvocation() {
JsInvocation jsClassDeclaration = new JsInvocation();
jsClassDeclaration.setQualifier(Namer.creationMethodReference());
return jsClassDeclaration;
private JsInvocation classCreateMethodInvocation(@NotNull JetClass jetClassDeclaration) {
if (jetClassDeclaration.isTrait()) {
return AstUtil.newInvocation(Namer.traitCreationMethodReference());
} else {
return AstUtil.newInvocation(Namer.classCreationMethodReference());
}
}
@NotNull
@@ -58,14 +58,18 @@ public final class Namer {
return name;
}
//TODO: dummy
public static JsNameRef classObjectReference() {
//TODO dummy
return AstUtil.newQualifiedNameRef("Class");
}
public static JsNameRef creationMethodReference() {
public static JsNameRef classCreationMethodReference() {
return AstUtil.newQualifiedNameRef("Class.create");
}
public static JsNameRef traitCreationMethodReference() {
return AstUtil.newQualifiedNameRef("Trait.create");
}
}
@@ -42,12 +42,26 @@ public class KotlinLibTest extends TranslationTest {
@Test
public void classObjectHasCreateMethod() throws Exception {
String objectName = "Class";
final Map<String, Class<? extends Scriptable>> propertyToType
= new HashMap<String, Class<? extends Scriptable>>();
propertyToType.put("create", Function.class);
runPropertyTypeCheck(objectName, propertyToType);
runPropertyTypeCheck("Class", propertyToType);
}
@Test
public void traitObjectHasCreateMethod() throws Exception {
final Map<String, Class<? extends Scriptable>> propertyToType
= new HashMap<String, Class<? extends Scriptable>>();
propertyToType.put("create", Function.class);
runPropertyTypeCheck("Trait", propertyToType);
}
@Test
public void createdTraitIsJSObject() throws Exception {
final Map<String, Class<? extends Scriptable>> propertyToType
= new HashMap<String, Class<? extends Scriptable>>();
runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("trait.js")),
new RhinoPropertyTypesChecker("foo", propertyToType));
}
}
@@ -56,6 +56,7 @@ public abstract class TranslationTest {
abstract protected List<String> generateFilenameList(String inputfile);
//TODO: refactor filename generation logic
private String getOutputFilePath(String filename) {
return getOutputDirectory() + convertToDotJsFile(filename);
}
@@ -68,6 +69,10 @@ public abstract class TranslationTest {
return getInputDirectory() + filename;
}
protected String cases(String filename) {
return getInputFilePath(filename);
}
protected void runFileWithRhino(String inputFile, Context context, Scriptable scope) throws Exception {
FileReader reader = new FileReader(inputFile);
context.evaluateReader(scope, reader, inputFile, 1, null);
@@ -0,0 +1,12 @@
foo = {};
(function(foo){
foo.Test = Trait.create({addFoo:function(s){
return s + 'FOO';
}
});
foo.ExtendedTest = Trait.create(foo.Test, {hooray:function(){
return 'hooray';
}
});
}
(foo));
+18 -3
View File
@@ -66,8 +66,6 @@ var Class = (function() {
return function() { return ancestor[m].apply(this, arguments); };
})(property).wrap(method);
// value.valueOf = method.valueOf.bind(method);
// value.toString = method.toString.bind(method);
}
this.prototype[property] = value;
}
@@ -85,8 +83,25 @@ var Class = (function() {
var Trait = (function() {
function add(object, source) {
properties = Object.keys(source);
for (var i = 0, length = properties.length; i < length; i++) {
var property = properties[i];
var value = source[property];
object[property] = value;
}
return this;
}
function create() {
return new Class.create(arguments);
result = {}
for (var i = 0, length = arguments.length; i < length; i++)
{
add(result, arguments[i]);
}
return result;
}
return {
+388
View File
@@ -0,0 +1,388 @@
function $A(iterable) {
if (!iterable) return [];
if ('toArray' in Object(iterable)) return iterable.toArray();
var length = iterable.length || 0, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
}
var emptyFunction = function() {}
var Class = (function() {
var IS_DONTENUM_BUGGY = (function(){
for (var p in { toString: 1 }) {
if (p === 'toString') return false;
}
return true;
})();
function subclass() {};
function create() {
var parent = null, properties = $A(arguments);
if (Object.isFunction(properties[0]))
parent = properties.shift();
function klass() {
this.initialize.apply(this, arguments);
}
Object.extend(klass, Class.Methods);
klass.superclass = parent;
klass.subclasses = [];
if (parent) {
subclass.prototype = parent.prototype;
klass.prototype = new subclass;
parent.subclasses.push(klass);
}
for (var i = 0, length = properties.length; i < length; i++)
klass.addMethods(properties[i]);
if (!klass.prototype.initialize)
klass.prototype.initialize = emptyFunction;
klass.prototype.constructor = klass;
return klass;
}
function addMethods(source) {
var ancestor = this.superclass && this.superclass.prototype,
properties = Object.keys(source);
if (IS_DONTENUM_BUGGY) {
if (source.toString != Object.prototype.toString)
properties.push("toString");
if (source.valueOf != Object.prototype.valueOf)
properties.push("valueOf");
}
for (var i = 0, length = properties.length; i < length; i++) {
var property = properties[i], value = source[property];
if (ancestor && Object.isFunction(value) &&
value.argumentNames()[0] == "$super") {
var method = value;
value = (function(m) {
return function() { return ancestor[m].apply(this, arguments); };
})(property).wrap(method);
value.valueOf = method.valueOf.bind(method);
value.toString = method.toString.bind(method);
}
this.prototype[property] = value;
}
return this;
}
return {
create: create,
Methods: {
addMethods: addMethods
}
};
})();
var Trait = (function() {
function create() {
var traitClass = Class.create.apply(Class, arguments)
return new traitClass;
}
return {
create: create
};
})();
(function() {
var _toString = Object.prototype.toString,
NULL_TYPE = 'Null',
UNDEFINED_TYPE = 'Undefined',
BOOLEAN_TYPE = 'Boolean',
NUMBER_TYPE = 'Number',
STRING_TYPE = 'String',
OBJECT_TYPE = 'Object',
FUNCTION_CLASS = '[object Function]',
BOOLEAN_CLASS = '[object Boolean]',
NUMBER_CLASS = '[object Number]',
STRING_CLASS = '[object String]',
ARRAY_CLASS = '[object Array]',
DATE_CLASS = '[object Date]';
function Type(o) {
switch(o) {
case null: return NULL_TYPE;
case (void 0): return UNDEFINED_TYPE;
}
var type = typeof o;
switch(type) {
case 'boolean': return BOOLEAN_TYPE;
case 'number': return NUMBER_TYPE;
case 'string': return STRING_TYPE;
}
return OBJECT_TYPE;
}
function extend(destination, source) {
for (var property in source)
destination[property] = source[property];
return destination;
}
function inspect(object) {
try {
if (isUndefined(object)) return 'undefined';
if (object === null) return 'null';
return object.inspect ? object.inspect() : String(object);
} catch (e) {
if (e instanceof RangeError) return '...';
throw e;
}
}
function toJSON(value) {
return Str('', { '': value }, []);
}
function Str(key, holder, stack) {
var value = holder[key],
type = typeof value;
if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
var _class = _toString.call(value);
switch (_class) {
case NUMBER_CLASS:
case BOOLEAN_CLASS:
case STRING_CLASS:
value = value.valueOf();
}
switch (value) {
case null: return 'null';
case true: return 'true';
case false: return 'false';
}
type = typeof value;
switch (type) {
case 'string':
return value.inspect(true);
case 'number':
return isFinite(value) ? String(value) : 'null';
case 'object':
for (var i = 0, length = stack.length; i < length; i++) {
if (stack[i] === value) { throw new TypeError(); }
}
stack.push(value);
var partial = [];
if (_class === ARRAY_CLASS) {
for (var i = 0, length = value.length; i < length; i++) {
var str = Str(i, value, stack);
partial.push(typeof str === 'undefined' ? 'null' : str);
}
partial = '[' + partial.join(',') + ']';
} else {
var keys = Object.keys(value);
for (var i = 0, length = keys.length; i < length; i++) {
var key = keys[i], str = Str(key, value, stack);
if (typeof str !== "undefined") {
partial.push(key.inspect(true)+ ':' + str);
}
}
partial = '{' + partial.join(',') + '}';
}
stack.pop();
return partial;
}
}
function stringify(object) {
return JSON.stringify(object);
}
function toQueryString(object) {
return $H(object).toQueryString();
}
function toHTML(object) {
return object && object.toHTML ? object.toHTML() : String.interpret(object);
}
function keys(object) {
if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
var results = [];
for (var property in object) {
if (object.hasOwnProperty(property)) {
results.push(property);
}
}
return results;
}
function values(object) {
var results = [];
for (var property in object)
results.push(object[property]);
return results;
}
function clone(object) {
return extend({ }, object);
}
function isElement(object) {
return !!(object && object.nodeType == 1);
}
function isArray(object) {
return _toString.call(object) === ARRAY_CLASS;
}
var hasNativeIsArray = (typeof Array.isArray == 'function')
&& Array.isArray([]) && !Array.isArray({});
if (hasNativeIsArray) {
isArray = Array.isArray;
}
function isHash(object) {
return object instanceof Hash;
}
function isFunction(object) {
return _toString.call(object) === FUNCTION_CLASS;
}
function isString(object) {
return _toString.call(object) === STRING_CLASS;
}
function isNumber(object) {
return _toString.call(object) === NUMBER_CLASS;
}
function isDate(object) {
return _toString.call(object) === DATE_CLASS;
}
function isUndefined(object) {
return typeof object === "undefined";
}
extend(Object, {
extend: extend,
inspect: inspect,
toQueryString: toQueryString,
toHTML: toHTML,
keys: Object.keys || keys,
values: values,
clone: clone,
isElement: isElement,
isArray: isArray,
isHash: isHash,
isFunction: isFunction,
isString: isString,
isNumber: isNumber,
isDate: isDate,
isUndefined: isUndefined
});
})();
Object.extend(Function.prototype, (function() {
var slice = Array.prototype.slice;
function update(array, args) {
var arrayLength = array.length, length = args.length;
while (length--) array[arrayLength + length] = args[length];
return array;
}
function merge(array, args) {
array = slice.call(array, 0);
return update(array, args);
}
function argumentNames() {
var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
.replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
.replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
}
function bind(context) {
if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
var __method = this, args = slice.call(arguments, 1);
return function() {
var a = merge(args, arguments);
return __method.apply(context, a);
}
}
function bindAsEventListener(context) {
var __method = this, args = slice.call(arguments, 1);
return function(event) {
var a = update([event || window.event], args);
return __method.apply(context, a);
}
}
function curry() {
if (!arguments.length) return this;
var __method = this, args = slice.call(arguments, 0);
return function() {
var a = merge(args, arguments);
return __method.apply(this, a);
}
}
function delay(timeout) {
var __method = this, args = slice.call(arguments, 1);
timeout = timeout * 1000;
return window.setTimeout(function() {
return __method.apply(__method, args);
}, timeout);
}
function defer() {
var args = update([0.01], arguments);
return this.delay.apply(this, args);
}
function wrap(wrapper) {
var __method = this;
return function() {
var a = update([__method.bind(this)], arguments);
return wrapper.apply(this, a);
}
}
function methodize() {
if (this._methodized) return this._methodized;
var __method = this;
return this._methodized = function() {
var a = update([this], arguments);
return __method.apply(null, a);
};
}
return {
argumentNames: argumentNames,
bind: bind,
bindAsEventListener: bindAsEventListener,
curry: curry,
delay: delay,
defer: defer,
wrap: wrap,
methodize: methodize
}
})());