UNPKG

24.2 kBJavaScriptView Raw
1// actor.js
2// actor style RPC using l8 and websockets/socket.io
3//
4// 2013/01/07 by JHR
5//
6// npm install socket.io
7// npm install zeparser
8// npm install socket.io-client
9// Also depends on https://github.com/natefaubion/matches.js
10// npm install matches
11
12// Boilerplate for module loaders. Basically: avoid polluting global space
13(function( define ){ // 'use strict'; JHR: 2013/01/18, commmented out, else it makes this undefined!
14define( function(){
15
16/*
17 * Dependencies
18 */
19
20var l8 = (this && this.l8) || require( "l8/lib/l8.js")
21var Pattern = require( "matches").pattern
22var SocketIo = require( "socket.io")
23var SocketIoClient = require( "socket.io-client")
24
25/*
26 * Debug
27 */
28
29this.__defineGetter__( "de", l8.getDebugFlag)
30var bug = l8.bug
31var mand = l8.mand
32
33
34/* ----------------------------------------------------------------------------
35 * Actors, local. Aka "active objects"
36 * See http://www.dalnefre.com/wp/2010/05/deconstructing-the-actor-model/
37 */
38
39var Registry = {}
40var RegistryIds = {}
41
42function Actor( name, delegate ){
43 this.name = name
44 this.task = null
45 this.queue = l8.queue() // aka "mailbox"
46 this.backlog = []
47 this.pattern = null
48 this.delegate = null
49 this.stage = null
50 var previous = null
51 if( (name[name.length - 1] != ".")
52 && (previous = this.lookup( name))
53 ){
54 previous.task.cancel()
55 }
56 this.name = this.register( name, this)
57 return this
58}
59var ProtoActor = Actor.prototype
60
61ProtoActor.toString = ProtoActor.toLabel
62= function(){ return "Actor/" + this.name }
63
64ProtoActor.register = function( name, object ){
65 if( !name ){ name = "." }
66 var dot = name.lastIndexOf( ".")
67 if( dot === -1 ){
68 Registry[name] = object
69 return name
70 }
71 var prefix = name.substr( 0, dot )
72 var suffix = name.substring( dot + 1)
73 if( suffix ){
74 Registry[name] = object
75 return name
76 }
77 var id = RegistryIds[prefix] || 0
78 name += id++
79 RegistryIds[prefix] = id
80 Registry[name] = object
81 return name
82}
83
84ProtoActor.lookup = function( name, remove ){
85 var obj = Registry[name]
86 if( obj && remove ){
87 delete Registry[name]
88 }
89 return obj
90}
91
92var SetPatternMatchOutcome
93
94ProtoActor.match = function( msg, delegate ){
95// Called when there is a message that the actor may process.
96// Returns false if the message must be queued.
97// Some messages require an answer (call type messages).
98// That answer is provided via a callback previous attached to the message.
99// To provide an answer now, the actor simply returns a value.
100// If that answer is a promise, the callback will be called when the promise
101// is either fullfilled or reject.
102// To provide an answer later, the actor must call the callback that it get
103// typically using this.actor.callback
104// Again, if that answer is a promise, the intial callback will be called when
105// the promise is either fullfilled or rejected.
106// While a call type message is beeing processed by the actor, no other such
107// messages can be processed, they are queued. However, once the actor got
108// access to the callback, such messages stop beeing queued, even if the
109// actor does call the callback right away but calls it later one.
110// All in all, this means that the actor can very easely specify how to handle
111// call messages, sync or async, in serie or in parallel.
112 var that = this
113 var callback = msg.caller
114 var callback_called = false
115 if( callback ){
116 // Skip "call type" message if a call is still beeing processed
117 if( that.caller )return false
118 // Safeguard the callback and handle case where a promise is delivered
119 callback = msg.callback
120 if( !callback ){
121 var true_cb = function( err, rslt ){
122 try{
123 if( callback_called ){
124 return
125 var error = new Error( "duplicate reply of " + that)
126 error.message = msg
127 throw error
128 }
129 callback_called = true
130 msg.caller( err, rslt)
131 }catch( e ){
132 l8.trace( "!!! callback failure in " + this, e)
133 }
134 }
135 msg.callback = callback = function( err, rslt ){
136 if( rslt && rslt.then ){
137 rslt.then(
138 function( ok ){ true_cb( 0, ok) },
139 function( ko ){ true_cb( ko) }
140 )
141 }else{
142 true_cb( err, rslt)
143 }
144 }
145 }
146 that.caller = callback
147 }
148 // When message went thru a ProxyActor it comes from a remote stage
149 this.stage = msg.remote
150 if( this.stage ){
151 de&&bug( "local actor " + this + " handles message from " + this.stage)
152 }
153 // Here is a message that maybe the actor is willing to process
154 var rslt
155 var err = null
156 // Let's try to process that message, either with a Role or patterns
157 if( delegate ){
158 try{
159 rslt = delegate.match( msg.message, msg.remote, msg.callback, this) // msg.message?
160 }catch( e ){
161 err = e
162 if( err === l8.continueError )return false
163 }
164 }else{
165 SetPatternMatchOutcome = function( e, r ){
166 err = e
167 rslt = r
168 }
169 try{
170 this.pattern.apply( this, msg.message)
171 }catch( e ){
172 return false
173 }
174 if( err === l8.continueEvent )return false
175 }
176 if( callback && callback === that.caller ){
177 that.caller = null
178 if( err ){
179 callback( err)
180 }else if( rslt || typeof rslt !== 'undefined' ){
181 callback( 0, rslt)
182 }
183 }
184 return true
185}
186
187ProtoActor.pick = function( delegate ){
188// When an unexpected message is received, it gets queued. Whenever the actor
189// attempts to receive a new message, these queued messages are proposed first.
190// Such queued messages are a backlog and it is the programmer's responsability
191// to make sure that the backlog does not grow much or else performances will
192// suffer.
193 var list = this.backlog
194 var len = list.length
195 if( !len )return false
196 var empty = false
197 var found = false
198 var msg
199 // Loop until nothing processable is found
200 while( true ){
201 // Scan queued messages, old ones first
202 for( var ii ; ii < len ; ii++ ){
203 // Skip emptry slot of removed messages
204 if( !(msg = list[ii]) )continue;
205 // The backlog is not empty, we'll have to wait to reset it
206 empty = false
207 if( !this.match( msg, delegate) )continue
208 found = true
209 list[ii] = null
210 break
211 }
212 // If a message was processed, restart scan, older messages first
213 if( !found )break
214 found = false
215 }
216 // When the backlog is made of empty slots, it's time to reset it
217 if( empty ){
218 this.backlog = []
219 }
220 return found
221}
222
223ProtoActor.act = function( delegate, after, timeout, loop ){
224// This the is where the heart of an actor beats.
225// The actor is normally idle, unless a timeout was specified.
226// If some revious message was left unprocessed, the actor will process
227// them if it wants. Or else, the actor waits for a new message to come in.
228// When the special timeout value 0 is specified, only old messages are
229// processed.
230// When the actor behavior depends on the reception of a single specific
231// message, once that message is received, the actor steps on (no loop).
232 var that = this
233 l8.repeat( function(){
234 // First, check backlog
235 this.step( function(){
236 if( that.pick( delegate) ){
237 if( loop )this.continue
238 this.break
239 }
240 // If no match, wait for a new message
241 }).step( function(){
242 if( timeout == 0 && that.queue.empty ){
243 if( after ){
244 after.call( that)
245 }
246 this.continue
247 }
248 that.queue.get()
249 // Either a match or add to backlog
250 }).step( function( msg ){
251 if( !that.match( msg, delegate) ){
252 de&&bug( "backlog") // ["backlog"].concat( msg))
253 that.backlog.push( msg)
254 }
255 if( !loop ) this.break
256 })
257 })
258 .failure( function( e ){
259 l8.trace( "Actor: " + that, "Unexpected error", e)
260 })
261}
262
263ProtoActor.__defineGetter__( "callback" , function(){
264 var caller = this.caller
265 this.caller = null
266 return caller
267})
268
269ProtoActor.receive = function( pattern, options ){
270// Define new behavior of actor using patterns
271 // Restore current pattern when task terminates
272 var previous_pattern = this.pattern
273 var that = this
274 if( !options ){
275 options = {}
276 }
277 this.task._task( function(){
278 this.defer( function(){ that.pattern = previous_pattern})
279 var after = options.after || pattern.after
280 var timeout = options.timeout || pattern.timeout
281 var loop = options.loop || pattern.loop
282 // When pattern can act, delegate to it
283 if( pattern.match ){
284 return that.act( pattern, after, timeout, loop)
285 }else if( pattern.delegate ){
286 return that.act( pattern.delegate, after, timeout, loop)
287 }
288 delete pattern.after
289 delete pattern.timeout
290 delete pattern.loop
291 // Encapsulate "matches.js" pattern to handle exceptions my way
292 for( var attr in pattern ){
293 if( attr === 'after' ){
294 after = pattern[attr]
295 continue
296 }
297 if( attr === 'timeout' ){
298 timeout = pattern[attr]
299 continue
300 }
301 pattern[attr] = (function( block){
302 return function(){
303 var rslt
304 var err = null
305 try{
306 rslt = block.apply( this, arguments)
307 }catch( e ){
308 err = e
309 }
310 SetPatternMatchOutcome( err, rslt)
311 }
312 })( pattern[attr])
313 }
314 that.pattern = Pattern( pattern)
315 that.act( null, after, timeout, loop)
316 })
317}
318
319ProtoActor.become = function( pattern, options ){
320 return this.receive( pattern, (options || {}).loop = true)
321}
322
323ProtoActor.send = function( message, caller ){
324// Send a message to this actor.
325// Optional 'caller' is a function( err, rslt) callback. If not provided,
326// a new promise is returned. The only possible error is when an attempt is
327// made to send a message to a dead/done actor.
328 var promise = null
329 if( !caller ){
330 promise = l8.promise()
331 caller = function( err, rslt ){
332 if( err ){
333 promise.reject( err)
334 }else{
335 promise.resolve( rslt)
336 }
337 }
338 }
339 var r = this.queue.tryPut( message.remote ? message : {message:message})
340 if( !r ){
341 caller( "Invalid send() on " + this)
342 }else{
343 caller( null, r)
344 }
345 return promise
346}
347
348ProtoActor.call = function( message, caller ){
349// Send a 'call type' message to this actor. The actor should reply.
350// optional 'caller' is a function( err, rslt) callback. If not provided,
351// a new promise is returned.
352 var promise = null
353 if( !caller ){
354 promise = l8.promise()
355 caller = function( err, rslt ){
356 if( err ){
357 promise.reject( err)
358 }else{
359 promise.resolve( rslt)
360 }
361 }
362 }
363 if( message.remote ){
364 message.caller = caller
365 }else{
366 message = {caller:caller,message:message}
367 }
368 var r = this.queue.tryPut( message)
369 if( !r ){
370 caller( "Invalid call() on " + this)
371 }
372 return promise
373}
374
375function MakeActorConstructor( name, pattern ){
376 return function(){
377 de&&bug( "create actor " + name)
378 var act = new Actor( name)
379 function byebye(){
380 act.queue.close()
381 // Deregister actor, unless another one already took over it
382 if( ProtoActor.lookup( name) === act ){
383 ProtoActor.register( name, null)
384 }
385 }
386 var task = l8._spawn( function(){
387 task.var( "actor", act)
388 task.step( function(){ act.receive( pattern, {loop:true}) })
389 task.step( function(){ byebye(); task.join() })
390 task.final( function(){ byebye() })
391 })
392 return act.task = task
393 }
394}
395
396
397/* ----------------------------------------------------------------------------
398 * Role class
399 * When an actor plays a role, it's role defines it's behavior based on the
400 * available methods of the role.
401 * This class can be the base class of user defined sub classes.
402 * Alternatively, one can instantiate a role with a delegate, in that case
403 * it's the delegate methods that define the ultimate behavior of the actor.
404 *
405 */
406
407function Role( delegate ){
408 this.delegate = delegate
409 var options = (delegate.options && delegate.options()) || {}
410 this.name = options.name
411 this.async = options.async
412 this.task = options.task
413 this.actor = null
414 this.stage = null
415 this.role = this
416}
417var ProtoRole = Role.prototype
418
419function MakeRole( delegate ){
420 return new Role( delegate )
421}
422
423ProtoRole.match = function( msg, remote, callback, actor ){
424 var that = this
425 MakeRole.current = that
426 that.actor = actor
427 that.stage = remote
428 that.callback = callback
429 var verb = msg[0]
430 function apply(){
431 var target = that
432 if( that.delegate != that ){
433 target = that.delegate
434 target.actor = actor
435 target.stage = remote
436 target.callback = callback
437 target.role = that
438 }
439 var target_method = target[verb]
440 if( target_method ){
441 msg.shift()
442 }else{
443 target_method = target["catch"]
444 }
445 return target_method.apply( target, msg)
446 }
447 if( !callback ){
448 if( !actor.task ){
449 return apply()
450 }
451 actor.task._spawn( function(){
452 return apply()
453 })
454 return
455 }
456 if( that.task ){
457 that.task._spawn( function(){
458 l8.step( function(){ return apply() })
459 l8.final( function( err, rslt ){
460 try{
461 if( !callback )return
462 callback( err, rslt)
463 }catch( e ){
464 l8.trace( "!!! unexpected callback error in " + actor, e)
465 }
466 })
467 })
468 return
469 }
470 var rslt
471 try{
472 rslt = apply()
473 if( !callback )return
474 if( typeof rslt === 'undefined' )return
475 if( rslt && rslt.then ){
476 rslt.then(
477 function( ok ){
478 callback( null, ok)
479 },
480 function( ko ){
481 callback( ko)
482 }
483 )
484 return
485 }
486 try{
487 callback( null, rslt)
488 }catch( e ){
489 l8.trace( "!!! unexpected callback error in " + actor, e)
490 }
491 }catch( err ){
492 try{
493 if( !callback )return
494 callback( err)
495 }catch( e ){
496 l8.trace( "!!! unexpected callback error in " + actor, e)
497 }
498 }
499 return
500}
501
502
503/* ---------------------------------------------------------------------------
504 * Stages, with actors in them. Each nodejs process (or browser) hosts a
505 * local stage and is connected to remote stages.
506 */
507
508var LocalStage = null
509var AllStages = {}
510var AllCalls = {}
511var NextCallbackId = 1
512var NextClientId = 1
513
514function Stage( name, address, not_lazy ){
515 this.name = name
516 var promise
517 this.promise = promise = l8.promise()
518 this.disconnected = l8.promise()
519 this.address = address
520 this.isLocal = typeof address !== 'string'
521 this.lazy = !not_lazy && !this.isLocal
522 this.resources = {}
523 AllStages[name] = this
524 var that = this
525 // Handle "local" stage, it hosts local actors
526 if( this.isLocal ){
527 AllStages["local"] = this
528 LocalStage = this
529 // Local stage running server side must listen for client connections
530 if( l8.server ){
531 this.listenSocket = null
532 this.allConnection = {}
533 // note, io.listen(<port>) will create a http server for you
534 try{
535 this.listenSocket = SocketIo.listen(
536 address || parseInt( process.env.PORT),
537 {"log level":1} // ToDo: options. See https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO
538 )
539 }catch( e ){
540 l8.trace( "Cannot listen for socket.io")
541 promise.reject()
542 }
543 promise.resolve( that)
544 this.listenSocket.sockets.on( 'connection', function( connection ){
545 var client_id = "client:" + NextClientId++
546 de&&bug( "new connection, " + client_id)
547 var stage = MakeStage( client_id, client_id)
548 stage.connection = connection
549 stage.promise.resolve( connection)
550 stage.setConnectionHandlers()
551 connection.on( 'message', function( msg ){
552 de&&bug( ["'send' from " + client_id].concat( msg))
553 })
554 connection.on( 'ack', function( msg ){
555 de&&bug( ["'ack' from " + client_id].concat( msg))
556 })
557 })
558 }
559 // Handle "remote" stage
560 }else{
561 if( !this.lazy ){
562 this.connect()
563 }
564 }
565 return this
566}
567
568var ProtoStage = Stage.prototype
569
570ProtoStage.toString = ProtoStage.toLabel
571= function(){ return "Stage/" + this.name }
572
573ProtoStage.connect = function(){
574 var that = this
575 var promise = this.promise
576 if( !this.lazy || this.isLocal )return this
577 this.lazy = false
578 // Handle remote stage based on address's syntax
579 var address = this.address
580 if( address.substr( 0, 7) === "client:" )return this
581 var url
582 var unix
583 var node
584 if( address.substr( 0, 7) === "http://" ){
585 url = address
586 }else if( address.substr( 0, 8) === "https://" ){
587 url = address
588 }else if( address.sibstr( 0, 5) === "node:" ){
589 node = address
590 }else{
591 cmd = address
592 }
593 // Handle urls with socket.io
594 if( url ){
595 var connection = SocketIoClient.connect( this.address, {})
596 // ToDo: options. See https://github.com/LearnBoost/socket.io-client
597 this.connection = connection
598 that.setConnectionHandlers()
599 connection.on( 'connect', function(){
600 that.promise.resolve( connection)
601 })
602 connection.on( 'connect_failed', function(){
603 that.connection = null
604 AllStages[that.name] = null
605 that.promise.reject( 'connect_failed')
606 that.disconnected.resolve()
607 })
608 // Handle node sub stages using process.fork()
609 }else if( node ){
610 throw Error( "not supported local l8 stage " + node)
611 // Handle unix sub process stages using process.spawn()
612 }else if( unix ){
613 throw Error( "not supported local l8 stage " + unix)
614 // ToDo: handle cmd with child processes
615 }else{
616 throw Error( "not supported local l8 stage " + address)
617 }
618 return this
619}
620
621ProtoStage.setConnectionHandlers = function(){
622 var that = this
623 var conn = this.connection
624 conn.on( 'send', function( msg ){
625 de&&bug( ["'send' from " + that].concat( msg))
626 var actor = ProtoActor.lookup( msg.name)
627 if( !actor ){
628 de&&bug( "'send' message for unknown " + msg.name + " actor")
629 return
630 }
631 actor.send( {remote:that,message:msg.send})
632 })
633 conn.on( 'call', function( msg ){
634 de&&bug( ["'call' from " + that].concat( msg))
635 var actor = ProtoActor.lookup( msg.name)
636 if( !actor ){
637 de&&bug( "'call' message for unknown " + msg.name + " actor")
638 conn.emit( "ack", [msg.caller, "bad actor"])
639 return
640 }
641 actor.call(
642 {remote:that,message:msg.call},
643 function( err, rslt ){
644 conn.emit( "ack", [msg.caller, err, rslt])
645 }
646 )
647 })
648 conn.on( 'ack', function( msg ){
649 de&&bug( ["'ack' from " + that].concat( msg))
650 var cb_id = msg[0]
651 var err = msg[1]
652 var rslt = msg[2]
653 if( err ){
654 AllCalls[cb_id].reject( err)
655 }else{
656 AllCalls[cb_id].resolve( rslt)
657 }
658 delete AllCalls[cb_id]
659 })
660 conn.on( 'disconnect', function( msg ){
661 de&&bug( ["'disconnect' from " + that].concat( msg))
662 that.promise.reject()
663 that.promise = l8.promise()
664 that.promise.reject()
665 that.disconnected.resolve()
666 delete AllStages[that.name]
667 })
668}
669
670ProtoStage.then = function( ok, ko ){
671 return this.connect().promise.then( ok, ko)
672}
673
674ProtoStage.defer = function( cb ){
675 var that = this
676 this.disconnected.then( function(){ cb.call( l8, that) })
677}
678
679ProtoStage.get = function( id ){
680 return this.resources[id]
681}
682
683ProtoStage.set = function( id, value ){
684 return this.resources[id] = value
685}
686
687function MakeStage( name, address, not_lazy ){
688 // Create local stage if never started so far
689 if( !LocalStage && name !== "local" ){
690 new Stage( name || "local")
691 }
692 // Return existing stage if possible
693 var stage = AllStages[name || "local"]
694 if( stage && (!address || stage.address === address) )return stage
695 // If local stage, let's rename it if never done before
696 if( !address && LocalStage && LocalStage.name === "local" ){
697 LocalStage.name = name
698 AllStages[name] = LocalStage
699 return LocalStage
700 }
701 // Else, create a connection to a new remote stage
702 if( name !== 'local' && !address )throw new Error( "Missing address for remote l8 stage")
703 var stage = new Stage( name, address, not_lazy)
704 return stage
705}
706
707
708/* ----------------------------------------------------------------------------
709 * ProxyActor is a proxy for an actor that lives in a remote stage.
710 * It provides .send() and .call() as regular actors do.
711 */
712
713var AllProxyActors = {}
714
715function ProxyActor( name, stage, address ){
716 if( stage ){
717 if( address ){
718 stage = MakeStage( stage, address)
719 }else if( (typeof stage === 'string') && (stage.indexOf( "://") !== -1) ){
720 stage = MakeStage( name, stage)
721 }
722 }
723 this.stage = stage || LocalStage
724 this.name = name
725 var stage_name = this.stage.name
726 AllProxyActors[stage_name + "/" + name] = this
727 return this
728}
729
730var ProtoProxyActor = ProxyActor.prototype
731
732function MakeProxyActor( name, stage, address ){
733 if( !LocalStage ){
734 new Stage( "local")
735 }
736 if( !stage ){ stage = LocalStage }
737 var proxy = AllProxyActors[stage.name + "/" + name]
738 if( proxy && proxy.stage === stage) return proxy
739 return new ProxyActor( name, stage, address)
740}
741
742ProtoProxyActor.toString = ProtoProxyActor.toLabel
743= function(){ return "Proxy/" + this.stage.name + "/" + this.name }
744
745ProtoProxyActor.send = function( args ){
746 var that = this
747 var promise = l8.promise()
748 this.stage.then(
749 function( conn ){
750 if( that.stage.isLocal ){
751 de&&bug( "local 'send' on " + that)
752 var actor = ProtoActor.lookup( that.name)
753 try{
754 actor.send.call( actor, args, l8.noop)
755 promise.resolve( that)
756 }catch( err ){
757 promise.reject( err)
758 }
759 return
760 }
761 try{
762 de&&bug( "'send' on " + that)
763 conn.emit( "send", {name:that.name,send:args})
764 promise.resolve( that)
765 }catch( err ){
766 promise.reject( err)
767 }
768 },
769 function( ko ){
770 l8.trace( "Could not 'send', unavailable stage " + this.stage)
771 promise.reject( ko)
772 }
773 )
774 return promise
775}
776
777ProtoProxyActor.call = function( args, caller ){
778 var that = this
779 var promise = l8.promise()
780 if( caller ){
781 promise.then(
782 function( ok ){ caller( null, ok) },
783 function( ko ){ caller( ko) }
784 )
785 }
786 this.stage.then(
787 function( conn ){
788 if( that.stage.isLocal ){
789 de&&bug( "local 'call' on " + that)
790 var actor = ProtoActor.lookup( that.name)
791 actor.call(
792 args,
793 function( err, rslt ){
794 if( err ){
795 promise.reject( err)
796 }else{
797 promise.resolve( rslt)
798 }
799 }
800 )
801 return
802 }
803 var cb_id = NextCallbackId++
804 AllCalls[cb_id] = promise
805 de&&bug( "'call' on " + that.stage)
806 conn.emit( "call", {name:that.name,call:args,caller:cb_id})
807 },
808 function( err ){ promise.reject( err) }
809 )
810 return promise
811}
812
813/* ---------------------------------------------------------------------------
814 * Campaign, where actors on multiple stages cooperate
815 */
816
817function Campaign( name ){
818 this.name = name
819 this.allServerStages = {}
820}
821var ProtoCampaign = Campaign.prototype
822
823ProtoCampaign.register = function( Stage ){
824 this.allServerStages[Stage.name] = Stage
825}
826
827ProtoCampaign.deregister = function( Stage ){
828 this.allServerStages[Stage.name] = null
829}
830
831ProtoCampaign.lookup = function( name ){
832 return this.allServerStages[name]
833}
834
835
836/*
837 * Exports are added to the existing l8 object
838 */
839
840l8.Actor = MakeActorConstructor
841l8.Actor.lookup = ProtoActor.lookup
842l8.Actor.all = Registry
843l8.proto.__defineGetter__( "actor", function(){ return this.get( "actor")})
844l8.Role = Role
845l8.role = MakeRole
846l8.stage = MakeStage
847l8.proxy = MakeProxyActor
848
849/*
850 * End boilerplate for module loaders
851 * Copied from when.js, see https://github.com/cujojs/when/blob/master/when.js
852 * Go figure what it means...
853 */
854
855return l8
856}) })(
857 typeof define == 'function' && define.amd
858 ? define
859 : function( factory ){
860 typeof exports === 'object'
861 ? (module.exports = factory())
862 : (this.l8 = factory());
863 }
864 );
865