design: added record interface
This commit is contained in:
parent
97f6ceb27b
commit
b42111a608
|
@ -1,3 +1,113 @@
|
|||
import {WatchGroup} from './watch_group';
|
||||
|
||||
|
||||
/**
|
||||
* Represents a Record for keeping track of changes. A change is a difference between previous
|
||||
* and current value.
|
||||
*
|
||||
* By default changes are detected using dirty checking, but a notifier can be present which can
|
||||
* notify the records of changes by means other than dirty checking. For example Object.observe
|
||||
* or events on DOM elements.
|
||||
*
|
||||
* DESIGN NOTES:
|
||||
* - No inheritance allowed so that code is monomorphic for performance.
|
||||
* - Atomic watch operations
|
||||
* - Defaults to dirty checking
|
||||
* - Keep this object as lean as possible. (Lean in number of fields)
|
||||
*
|
||||
* MEMORY COST: 13 Words;
|
||||
*/
|
||||
export class Record {
|
||||
|
||||
}
|
||||
@FIELD('final _watchGroup:WatchGroup')
|
||||
@FIELD('final _protoRecord:ProtoRecord')
|
||||
@FIELD('_context')
|
||||
@FIELD('_getter')
|
||||
@FIELD('_arguments')
|
||||
@FIELD('_previousValue')
|
||||
@FIELD('_mode:int')
|
||||
/// order list of all records. Including head/tail markers
|
||||
@FIELD('_next:Record')
|
||||
@FIELD('_prev:Record')
|
||||
/// next record to dirty check
|
||||
@FIELD('_checkNext:Record')
|
||||
@FIELD('_checkPrev:Record')
|
||||
// next notifier
|
||||
@FIELD('_notifierNext:Record')
|
||||
// notifier context will be present to the notifier to release
|
||||
// the object from notification/watching.
|
||||
@FIELD('_notifierContext')
|
||||
// Opeque data which will be presented to WatchGroupDispatcher
|
||||
@FIELD('_watchContext')
|
||||
// IF we detect change, we have to update the _context of the
|
||||
// next record.
|
||||
@FIELD('_updateContext:Record')
|
||||
// May be removed if we don't support coelsence.
|
||||
@FIELD('_updateContextNext:Record')
|
||||
constructor() {
|
||||
}
|
||||
|
||||
check():bool {
|
||||
var mode = this.mode;
|
||||
var state = mode & MODE_MASK_STATE;
|
||||
var notify = mode & MODE_MASK_NOTIFY;
|
||||
var currentValue;
|
||||
switch (state) {
|
||||
case MODE_STATE_MARKER:
|
||||
return false;
|
||||
case MODE_STATE_PROPERTY:
|
||||
currentValue = this._getter(this._context);
|
||||
break;
|
||||
case MODE_STATE_INVOKE_CLOSURE:
|
||||
currentValue = this._context(this._arguments);
|
||||
break;
|
||||
case MODE_STATE_INVOKE_METHOD:
|
||||
currentValue = this._getter(this._context, this._arguments);
|
||||
break;
|
||||
case MODE_STATE_MAP:
|
||||
case MODE_STATE_LIST:
|
||||
}
|
||||
var previousValue = this._previousValue;
|
||||
if (isSame(previousValue, currentValue)) return false;
|
||||
if (previousValue instanceof String && currentValue instanceof String
|
||||
&& previousValue == currentValue) {
|
||||
this._previousValue = currentValue;
|
||||
return false
|
||||
}
|
||||
this.previousValue = previousValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The mode is devided into two partes. Which notification mechanism
|
||||
// to use and which dereference mode to execute.
|
||||
|
||||
// We use dirty checking aka no notification
|
||||
var MODE_MASK_NOTIFY:number = 0xFF00;
|
||||
// Encodes the state of dereference
|
||||
var MODE_MASK_STATE:int = 0x00FF;
|
||||
|
||||
var MODE_PLUGIN_DIRTY_CHECK:int = 0x0000;
|
||||
var MODE_STATE_MARKER:int = 0x0000;
|
||||
|
||||
/// _context[_protoRecord.propname] => _getter(_context)
|
||||
var MODE_STATE_PROPERTY:int = 0x0001;
|
||||
/// _context(_arguments)
|
||||
var MODE_STATE_INVOKE_CLOSURE:int = 0x0002;
|
||||
/// _getter(_context, _arguments)
|
||||
var MODE_STATE_INVOKE_METHOD:int = 0x0003;
|
||||
|
||||
/// _context is Map => _previousValue is MapChangeRecord
|
||||
var MODE_STATE_MAP:int = 0x0004;
|
||||
/// _context is Array/List/Iterable => _previousValue = ListChangeRecord
|
||||
var MODE_STATE_LIST:int = 0x0005;
|
||||
|
||||
function isSame(a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
} else if ((a !== a) && (b !== b)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@ export * from './annotations/directive';
|
|||
export * from './annotations/component';
|
||||
export * from './annotations/template_config';
|
||||
|
||||
export * from 'change_detection/change_detection';
|
||||
export * from 'change_detection/record';
|
||||
|
||||
export * from './compiler/compiler';
|
||||
export * from './compiler/template_loader';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Directive} from './directive';
|
||||
|
||||
export class Component extends Directive {
|
||||
@CONST constructor({
|
||||
constructor({
|
||||
selector,
|
||||
template,
|
||||
elementServices,
|
||||
|
|
|
@ -3,7 +3,7 @@ import {ElementServicesFunction} from './facade';
|
|||
|
||||
@ABSTRACT
|
||||
export class Directive {
|
||||
@CONST constructor({
|
||||
constructor({
|
||||
selector,
|
||||
lightDomServices,
|
||||
implementsTypes
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Type, List} from 'facade/lang';
|
||||
|
||||
export class TemplateConfig {
|
||||
@CONST constructor({
|
||||
constructor({
|
||||
url,
|
||||
directives,
|
||||
formatters,
|
||||
|
|
|
@ -6,5 +6,5 @@ export class ProtoView {
|
|||
@FIELD('final _module:Module')
|
||||
@FIELD('final _protoElementInjectors:List<ProtoElementInjector>')
|
||||
@FIELD('final _protoWatchGroup:ProtoWatchGroup')
|
||||
@CONST constructor() { }
|
||||
constructor() { }
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -2,8 +2,15 @@ function same(a, b) {
|
|||
return a === b;
|
||||
}
|
||||
|
||||
function notSame(a, b) {
|
||||
if ((a !== a) && (b !== b)) return true;
|
||||
return a !== b;
|
||||
}
|
||||
|
||||
function main() {
|
||||
var obj = {};
|
||||
assert(same({}, {}) == false);
|
||||
assert(same(obj, obj) == true);
|
||||
assert(notSame({}, {}) == true);
|
||||
assert(notSame(obj, obj) == false);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ function namedObjectType({a,b}:{a:A,b:B<C>}) {
|
|||
}
|
||||
|
||||
class Bar {
|
||||
@CONST constructor({
|
||||
constructor({
|
||||
selector,
|
||||
lightDomServices,
|
||||
implementsTypes
|
||||
|
@ -47,6 +47,7 @@ function main() {
|
|||
// TODO(vojta): test this better.
|
||||
var f = new Foo(1, 2);
|
||||
assert(f.sum() == 3);
|
||||
assert(f instanceof Foo);
|
||||
|
||||
f.typedVariables();
|
||||
}
|
|
@ -5,7 +5,7 @@ import {createVariableStatement, createCallExpression, createIdentifierExpressio
|
|||
// var CONSTRUCTOR = token.CONSTRUCTOR;
|
||||
|
||||
import {PROPERTY_METHOD_ASSIGNMENT, MEMBER_EXPRESSION, THIS_EXPRESSION, BINARY_EXPRESSION} from 'traceur/src/syntax/trees/ParseTreeType';
|
||||
import {EQUAL_EQUAL_EQUAL} from 'traceur/src/syntax/TokenType';
|
||||
import {EQUAL_EQUAL_EQUAL, NOT_EQUAL_EQUAL} from 'traceur/src/syntax/TokenType';
|
||||
import {CONSTRUCTOR} from 'traceur/src/syntax/PredefinedName';
|
||||
|
||||
import {VariableStatement, VariableDeclarationList} from 'traceur/src/syntax/trees/ParseTrees';
|
||||
|
@ -61,12 +61,23 @@ export class ClassTransformer extends ParseTreeTransformer {
|
|||
// Transform triple equals into identical() call.
|
||||
// TODO(vojta): move to a separate transformer
|
||||
transformBinaryExpression(tree) {
|
||||
if (tree.operator.type === EQUAL_EQUAL_EQUAL) {
|
||||
tree.left = this.transformAny(tree.left);
|
||||
tree.right = this.transformAny(tree.right);
|
||||
if (tree.operator.type === 'instanceof') {
|
||||
// a instanceof b -> a is b
|
||||
// TODO(vojta): do this in a cleaner way.
|
||||
tree.operator.type = 'is';
|
||||
return tree;
|
||||
} else if (tree.operator.type === EQUAL_EQUAL_EQUAL) {
|
||||
// a === b -> identical(a, b)
|
||||
return createCallExpression(createIdentifierExpression('identical'), createArgumentList([tree.left, tree.right]));
|
||||
} else if (tree.operator.type === NOT_EQUAL_EQUAL) {
|
||||
// a !== b -> !identical(a, b)
|
||||
// TODO(vojta): do this in a cleaner way.
|
||||
return createCallExpression(createIdentifierExpression('!identical'), createArgumentList([tree.left, tree.right]));
|
||||
} else {
|
||||
return tree;
|
||||
}
|
||||
|
||||
return tree;
|
||||
};
|
||||
|
||||
transformClassDeclaration(tree) {
|
||||
|
|
Loading…
Reference in New Issue