Skip to content

Commit

Permalink
Merge pull request #122 from janvennemann/TIMOB-24009-2_0_X
Browse files Browse the repository at this point in the history
[TIMOB-24009] Properly generate properties and methods from protocols (2_0_X)
  • Loading branch information
hansemannn committed Mar 3, 2017
2 parents de5bb98 + 2236fda commit 93ff33b
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 13 deletions.
7 changes: 4 additions & 3 deletions metabase/ios/Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ module.exports = function (grunt) {
ignoreLeaks: false,
globals: ['Hyperloop', 'HyperloopObject']
},
src: ['test/**/*_test.js'],
src: ['test/**/*_test.js']
},
jshint: {
options: {
jshintrc: true
jshintrc: true,
force: true
},
src: ['*.js','lib/**/*.js','test/**/*.js']
src: ['*.js', 'lib/**/*.js']
},
kahvesi: {
src: ['test/**/*.js']
Expand Down
91 changes: 82 additions & 9 deletions metabase/ios/lib/generate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,85 @@ function merge (src, dest) {
}
}

function superClassImplementsProxy (json, cls, proto) {
var prev;
while (cls && cls.superclass) {
prev = cls;
cls = cls.superclass;
if (cls) {
cls = json.classes[cls];
/**
* Checks if a parent class in the inheritance path already implemented
* the given protocol.
*
* @param {Object} json Native code metabase
* @param {Object} cls Class to traverse upwards from
* @param {String} proto The protocol to look for in parent classes
* @return {bool} True if protocol already implemented in a parent class, false otherwise.
*/
function isProtocolImplementedBySuperClass (json, cls, proto) {
var parentClass = cls && cls.superclass;
while (parentClass) {
if (parentClass.protocols && parentClass.protocols.indexOf(proto) !== -1) {
return true;
}
parentClass = parentClass.superclass ? json.classes[parentClass.superclass] : null;
}
return (prev && prev.protocols && prev.protocols.indexOf(proto) !== -1);

return false;
}

/**
* Iterates over all given protocols and processes protocol inheritance by
* incorporating one protocol into another through merging their methods and
* properties.
*
* @param {Object} protocols Object with protocols from the metabase
*/
function processProtocolInheritance (protocols) {
var mergedProtocols = [];
/**
* Recursively merges a protocol with all it's inherited protocols
*
* @param {Object} protocol A protocol
* @param {Number} logIntendationLevel Intendation level for debugging messages
*/
function mergeWithParentProtocols(protocol, logIntendationLevel) {
var logIntendationCharacter = " ";
var logIntendation = logIntendationCharacter.repeat(logIntendationLevel++);
var parentProtocols = protocol.protocols;
var protocolSignature = parentProtocols ? protocol.name + ' <' + parentProtocols.join(', ') + '>' : protocol.name;
util.logger.trace(logIntendation + 'Processing inherited protocols of ' + protocolSignature);
logIntendation = logIntendationCharacter.repeat(logIntendationLevel);

if (mergedProtocols.indexOf(protocol.name) !== -1) {
util.logger.trace(logIntendation + protocol.name + ' was already merged with all protocols it inherits from.');
return;
}
if (!parentProtocols) {
util.logger.trace(logIntendation + protocol.name + ' does not inherit from any other protocols.');
mergedProtocols.push(protocol.name);
return;
}

util.logger.trace(logIntendation + 'Iterating over inherited protocols of ' + protocol.name);
logIntendationLevel++;
protocol.protocols.forEach(function (parentProtocolName) {
if (protocol.name === parentProtocolName) {
util.logger.trace(logIntendation + 'Invalid protocol meta information. ' + protocol.name.red + ' cannot have itself as parent, skipping.');
return;
}
var parentProtocol = protocols[parentProtocolName];
mergeWithParentProtocols(parentProtocol, logIntendationLevel);

util.logger.trace(logIntendation + 'Merging ' + parentProtocol.name.cyan + ' => ' + protocol.name.cyan);
protocol.properties = protocol.properties || {};
protocol.methods = protocol.methods || {};
merge(parentProtocol.properties, protocol.properties);
merge(parentProtocol.methods, protocol.methods);
});

mergedProtocols.push(protocol.name);
}

Object.keys(protocols).forEach(function (protocolName) {
var protocol = protocols[protocolName];
var logIntendationLevel = 0;
mergeWithParentProtocols(protocol, logIntendationLevel);
});
}

function generateBuiltins (json, callback) {
Expand Down Expand Up @@ -146,6 +215,8 @@ function generateFromJSON (name, dir, json, state, callback, includes) {
}
}

processProtocolInheritance(json.protocols);

// classes
Object.keys(json.classes).forEach(function (k) {
var cls = json.classes[k];
Expand All @@ -155,11 +226,13 @@ function generateFromJSON (name, dir, json, state, callback, includes) {
// add protocols
if (cls.protocols && cls.protocols.length) {
cls.protocols.forEach(function (p) {
if (superClassImplementsProxy(json, cls, p)) {
if (isProtocolImplementedBySuperClass(json, cls, p)) {
return;
}
var protocol = json.protocols[p];
if (protocol) {
cls.properties = cls.properties || {};
cls.methods = cls.methods || {};
merge(protocol.properties, cls.properties);
merge(protocol.methods, cls.methods);
}
Expand Down
1 change: 1 addition & 0 deletions metabase/ios/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"ejs": "^2.3.4",
"escodegen": "^1.7.0",
"plist": "^1.1.0",
"rewire": "^2.5.2",
"semver": "^5.0.3",
"walk-ast": "0.0.2",
"wrench": "^1.5.8"
Expand Down
12 changes: 12 additions & 0 deletions metabase/ios/test/fixtures/protocol_inheritance.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@protocol BaseProtocol <NSObject>

@property float a;
- (void)b;

@end

@protocol MyProtocol <BaseProtocol>

- (void)c;

@end
134 changes: 133 additions & 1 deletion metabase/ios/test/protocol_test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
var should = require('should'),
var assert = require('assert'),
rewire = require('rewire'),
should = require('should'),
helper = require('./helper');

describe('protocol', function () {
Expand Down Expand Up @@ -196,4 +198,134 @@ describe('protocol', function () {
done();
});
});

it('should merge incorporated protocol methods and properties', function(done) {
helper.generate(helper.getFixture('protocol_inheritance.h'), helper.getTempFile('protocol.json'), function (err, json, sdk) {
assert.ifError(err);

var baseProtocol = {
filename: helper.getFixture('protocol_inheritance.h'),
framework: 'fixtures',
line: '1',
methods: {
a: {
arguments: [],
encoding: 'f16@0:8',
instance: true,
name: 'a',
returns: { encoding: 'f', type: 'float', value: 'float' },
selector: 'a'
},
b: {
arguments: [],
encoding: 'v16@0:8',
instance: true,
name: 'b',
returns: { encoding: 'v', type: 'void', value: 'void' },
selector: 'b'
},
'setA:': {
arguments: [
{ encoding: 'f', name: 'a', type: 'float', value: 'float' }
],
encoding: 'v20@0:8f16',
instance: true,
name: 'setA',
returns: { encoding: 'v', type: 'void', value: 'void' },
selector: 'setA:'
}
},
name: 'BaseProtocol',
properties: {
a: {
name: 'a',
optional: false,
type: { type: 'float', value: 'float' }
}
},
thirdparty: true
};
should(json).have.property('protocols', {
BaseProtocol: baseProtocol,
MyProtocol: {
filename: helper.getFixture('protocol_inheritance.h'),
framework: 'fixtures',
line: '8',
methods: {
c: {
arguments: [],
encoding: 'v16@0:8',
instance: true,
name: 'c',
returns: { encoding: 'v', type: 'void', value: 'void' },
selector: 'c'
}
},
name: 'MyProtocol',
protocols: [ 'BaseProtocol' ],
thirdparty: true
}
});

var generate = rewire('../lib/generate/index');
var processIncorporatedProtocols = generate.__get__('processIncorporatedProtocols');
processIncorporatedProtocols(json.protocols);
should(json).have.property('protocols', {
BaseProtocol: baseProtocol,
MyProtocol: {
filename: helper.getFixture('protocol_inheritance.h'),
framework: 'fixtures',
line: '8',
methods: {
a: {
arguments: [],
encoding: 'f16@0:8',
instance: true,
name: 'a',
returns: { encoding: 'f', type: 'float', value: 'float' },
selector: 'a'
},
b: {
arguments: [],
encoding: 'v16@0:8',
instance: true,
name: 'b',
returns: { encoding: 'v', type: 'void', value: 'void' },
selector: 'b'
},
c: {
arguments: [],
encoding: 'v16@0:8',
instance: true,
name: 'c',
returns: { encoding: 'v', type: 'void', value: 'void' },
selector: 'c'
},
'setA:': {
arguments: [
{ encoding: 'f', name: 'a', type: 'float', value: 'float' }
],
encoding: 'v20@0:8f16',
instance: true,
name: 'setA',
returns: { encoding: 'v', type: 'void', value: 'void' },
selector: 'setA:'
}
},
name: 'MyProtocol',
properties: {
a: {
name: 'a',
optional: false,
type: { type: 'float', value: 'float' }
}
},
protocols: [ 'BaseProtocol' ],
thirdparty: true
}
});

done();
});
});
});

0 comments on commit 93ff33b

Please sign in to comment.