/
one_web.js
1450 lines (1332 loc) · 40.3 KB
/
one_web.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"use strict"
// Copyright (C) 2014 OneJS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ONEJS boot up fabric for webbrowser
// toggle fake worker on or off
ONE.fake_worker = false
ONE.ignore_cache = false
ONE.prototype_mode = true
ONE.compress_cache = false
ONE.hammer_time = 50 // its about as bad an idea as the song
ONE.hacklog = function(txt){
if(document.body)
document.body.innerHTML += '<span style="color:#fff">'+txt+'</span> '
}
ONE.worker_boot_ = function(host){
function delayRunMessage(msg){
return function(){
var dt = Date.now()
if(typeof msg.proxify_cache == 'object') host.proxify_cache = msg.proxify_cache
ONE.__modules__[msg.module_name].compiled_function.call(ONE.root)
ONE.total_run = Date.now() -dt
setTimeout(host.writeModuleCache, 10)
}
}
host.onmessage = function(event){
var data = event.data
if(!Array.isArray(data)) throw new Error('non array message received')
for(var i = 0, l = data.length; i < l; i++){
var msg = data[i]
if(msg._type == 'signal'){ // we have to set a value
var obj = host.proxy_obj[msg._uid]
if(!obj) throw new Error('set on nonexistant object ' + msg._uid)
var value = ONE.proxyDataUnescape(msg.value, host.proxy_obj) || msg.value
obj[msg.name] = value
}
else if(msg._type == 'call'){
var obj = host.proxy_obj[msg._uid]
if(!obj) throw new Error('Call on nonexistant object ' + msg._uid)
if(msg.args) obj[msg.name].apply(obj, msg.args)
else obj[msg.name].call(obj)
}
else if (msg._type == 'return'){
var sig = host.call_signals[msg.callid]
if(!sig) throw new Error('Return on nonexistant signal ')
host.call_free.push(msg.callid)
host.call_signals[msg.callid] = undefined
var obj = ONE.proxyDataUnescape(msg.ret, host.proxy_obj)
if(obj) msg.ret = obj
sig.end(msg.ret)
}
else if(msg._type == 'eval'){ // lets parse and eval a module
var module = ONE.__modules__[msg.module_name]
var ast = module.ast
var dt = Date.now()
if(!ast){
return console.log('Module ' + msg.module_name + ' not parsed')
}
module.compiled_function = ONE.root.eval(ast, msg.module_name)
// lets push this on our cache queue
ONE.compile_cache_queue.push(module)
var ms = Date.now()-dt
ONE.total_eval += ms
}
else if(msg._type == 'parse'){
var dt = Date.now()
var module = ONE.__modules__[msg.module_name] = Object.create(null)
module.name = msg.module_name
module.source_code = msg.value
module.ast = ONE.root.parse('->{' + msg.value + '\n}', msg.module_name)
var ms = Date.now()-dt
ONE.total_parse += ms
}
else if(msg._type == 'run'){
if(ONE.import_queue) ONE.import_complete = delayRunMessage(msg)
else delayRunMessage(msg)()
}
else if(msg._type == 'import'){
ONE.importFetch(msg.url, msg.responseType)
}
else if(msg._type == 'eval_cached'){
var module = ONE.root.deserializeModule(msg.module_name, msg.value)
}
}
host.postProcess()
}
// module cache
host.writeModuleCache = function(){
console.log('load time ' + (ONE.total_eval + ONE.total_parse + ONE.total_run + ONE.total_proxify+ ONE.total_deserialize + ONE.total_compile) + ' ' +
'eval: ' + ONE.total_eval + ' parse:'+ONE.total_parse + ' run:'+ONE.total_run + ' proxify:'+ONE.total_proxify + ' total_deserialize:'+ONE.total_deserialize+ ' total_compile:'+ONE.total_compile)
var queue = ONE.compile_cache_queue
// lets encode all our modules and send them over to the main thread for caching.
for(var i = 0;i<queue.length;i++){
var module = queue[i]
var blob = ONE.Base.AST.serializeModule(module)
if(ONE.compress_cache)
blob = ONE.deflate(blob, {level:6, to:'string'})
host.sendToHost({_type:'module_cache', key:module.source_code, value:blob})
}
}.bind(host)
host.call_id = 0
host.call_signals = {}
host.call_free = []
// message queueing
host.msg_queue = []
host.msg_transfer = []
host.msg_sigblk = []
//var id = 1
host.transferToHost = function(data, name){
if(data.transferd) return
data.transfered = 1
return this.msg_transfer.push(data)
}
host.sendToHost = function(msg, sigblk){
if(sigblk) this.msg_sigblk.push(sigblk, msg)
this.msg_queue.push(msg)
}
// Set immediate
function _setImmediate(cb){
return host.immediate_queue.push(cb) + 1
}
host.immediate_queue = []
if(typeof window !== 'undefined') window.setImmediate = _setImmediate
else if(typeof global !== 'undefined') global.setImmediate = _setImmediate
else self.setImmediate = _setImmediate
// Set interval
var __setInterval = setInterval
function _setInterval(cb, time){
return __setInterval(function(){
cb()
host.postProcess()
}, time)
}
if(typeof window !== 'undefined') window.setInterval = _setInterval
else if(typeof global !== 'undefined') global.setInterval = _setInterval
else self.setInterval = _setInterval
// Set timeout
var __setTimeout = setTimeout
function _setTimeout(cb, time){
return __setTimeout(function(){
cb()
host.postProcess()
}, time)
}
if(typeof window !== 'undefined') window.setTimeout = _setTimeout
else if(typeof global !== 'undefined') global.setTimeout = _setTimeout
else self.setTimeout = _setTimeout
// vector queue, used to auto transfer typed vectors when their length changes
host.vector_queue = []
// proxification
host.proxy_obj = {}
host.proxify_queue = []
host.proxify_cache = {}
host.proxy_uid = 1
host.proxy_free = []
// do all the things we need to do post a code execution
host.postProcess = function(){
// process proxification
while(host.proxify_queue.length || host.vector_queue.length || host.msg_queue.length || host.immediate_queue.length){
if(host.proxify_queue.length){
var queue = host.proxify_queue
host.proxify_queue = []
var dt = Date.now()
for(var i = 0, l = queue.length; i<l; i++){
queue[i]._proxify()
}
ONE.total_proxify += Date.now() - dt
}
// process vector queue
if(host.vector_queue.length){
var queue = host.vector_queue
host.vector_queue = []
for(var i = 0;i<queue.length;i++){
var q = queue[i]
// call the setter to transfer
q._bind_[q._key_] = q
}
}
// process the message queue
if(host.msg_sigblk.length){
var queue = host.msg_sigblk
for(var i = 0;i<queue.length;i+=2){
queue[i]['__setvalue_'+queue[i+1].name] = undefined
}
host.msg_sigblk = []
}
// first flush what we have
if(host.msg_queue.length){
var queue = host.msg_queue
var transfer = host.msg_transfer
host.msg_transfer = []
host.msg_queue = []
host.postMessage(queue, transfer)
}
// execute all setImmediates
if(host.immediate_queue.length){
var queue = host.immediate_queue
host.immediate_queue = []
for(var i = 0; i<queue.length; i++){
queue[i]()
}
}
}
}
ONE.import_queue = 0
ONE.import_complete
ONE.importFetch = function(url, responseType){
ONE.import_queue++
var req = new XMLHttpRequest()
var full_url = ONE.origin + '/' + url
req.open("GET", full_url, true)
req.responseType = responseType
req.onreadystatechange = function(){
if(req.readyState == 4){
if(req.status != 200){
}
else{
var mod = ONE.__modules__[url] = {
name:url,
instance:req.response || req.responseText
}
if(typeof mod.instance == 'object')
mod.instance.name = url
}
if(!--ONE.import_queue && ONE.import_complete){
ONE.import_complete()
host.postProcess()
}
}
}
req.send()
}
ONE.compile_cache_queue = []
// statistics
ONE.total_eval = 0
ONE.total_proxify = 0
ONE.total_parse = 0
ONE.total_deserialize = 0
ONE.total_compile = 0
ONE.host = host
ONE.init()
ONE.init_ast()
ONE.Base.thisInterval = function(cb, time){
var pthis = this
return __setInterval(function(){
cb.call(pthis)
host.postProcess()
}, time)
}
ONE.Base.thisTimeout = function(cb, time){
var pthis = this
return __setTimeout(function(){
cb.call(pthis)
host.postProcess()
}, time)
}
ONE.Base.hideProperties()
ONE.root = ONE.Base.new()
ONE.root.__class__ = 'Root'
}
ONE.proxy_ = function(){
// baseclass for the worker objects
this.Base.WorkerProxy = this.Base.extend(function(){
this.__proxy__ = 0
this.__class__ = 'WorkerProxy'
// called when someone makes an instance
this.prestructor = function(){
if(!ONE.host.proxy_free.length) this.__proxy__ = ONE.host.proxy_uid++
else this.__proxy__ = ONE.host.proxy_free.pop()
this.defineProperty('__proxy__', { enumerable:false, configurable:true })
// store it
ONE.host.proxy_obj[this.__proxy__] = this
// queue up our object proxify
ONE.host.proxify_queue.push(this)
}
// make sure extend pre and post dont fire on us
var blockPrePost = this
// called in .extend to make sure we proxify before being used
// this is necessary otherwise the getter/setter layer never works
this._extendPre = function(){
if(this == blockPrePost) return // make sure our current.extend doesnt trigger us
if(!ONE.host.proxy_free.length) this.__proxy__ = ONE.host.proxy_uid++
else this.__proxy__ = ONE.host.proxy_free.pop()
this.defineProperty('__proxy__', { enumerable:false, configurable:true })
// store it
ONE.host.proxy_obj[this.__proxy__] = this
}
this._extendPost = function(){
if(this == blockPrePost) return // make sure our current.extend doesnt trigger us
this.Base._extendPost.call(this)
this._proxify()
}
// the main property proxy on the worker side
this._propertyProxy = function(name){
if(this.__lookupSetter__(name)) return
var store = '__' + name
var sigblk = '__setvalue_' + name
this[store] = this[name]
if(!this.__lookupSetter__(name)) Object.defineProperty(this, name, {
get:function(){
return this[store]
},
set:function(v){
var old = this[store]
this[store] = v
var sigmsg = this[sigblk]
// fastpath to swallow multiple value changes in one execution
if(sigmsg && sigmsg._type == 'setvalue' && (!v || !v._transfer_)){
console.log('blocking signal')
sigmsg.value = v
return
}
if(this.hasOwnProperty('__compilehash__')){
// if we switch from value to astnode and back we need a recompile
if(v && v.__proxy__/* || old && old.__proxy__*/){
return ONE.host.sendToHost({_type:'setref', _uid:this.__proxy__, name:name, value:v.__proxy__})
}
var recompile = false
if(v && v._ast_){
recompile = true
}
else if(old && old._ast_) recompile = true
if(!recompile){
var msg = {_type:'setvalue', _uid:this.__proxy__, name:name}
this[sigmsg] = msg
if(typeof v !== 'object'){
msg.value = v
}
else if(v._t_){
if(v._transfer_) msg.value = v._transfer_(ONE.host)
else msg.value = v
}
return ONE.host.sendToHost(msg, this)
}
// line us up for reproxification
if(!this._proxify_flag){
this._proxify_flag = true
ONE.host.proxify_queue.push(this)
}
}
}
})
}
this._compilePropBinds = function(){
var hasbinds = false
var binds = this.proxy_refs
var init = 'this._initBinds = function(){\n'
var deinit = 'this._deinitBinds = function(){\n'
for(var bind in binds){
hasbinds = true
var props = binds[bind]
for(var prop in props){
init += '\tthis._bindProp(this.'+bind+',"'+prop+'")\n'
deinit += '\tthis._unbindProp(this.'+bind+',"'+prop+'")\n'
}
}
if(!hasbinds) return ''
init += '}\n'
deinit += '}\n'
// alright. how does this propertybinding crap look like.
// alright so, we wanna bind to
return init+deinit
}
// proxify builds the message that spawns and updates proxified objects
// on the host side.
this._proxify = function(){
// create a proxy id
this._proxify_flag = false
var proto = Object.getPrototypeOf(this)
var isupdate = this.hasOwnProperty('__compilehash__')
// iterate the keys we have
var comp
var msg = {_type:'proxify', _proto:proto.__proxy__, _uid:this.__proxy__}
var transfers = []
var hash = proto.__compilehash__ || ""
var keys = Object.keys(this)
var methods = ""
for(var i = 0, l = keys.length; i < l; i++){
var name = keys[i]
// skip all getters and setters
if((this.__lookupSetter__(name) ||
this.__lookupGetter__(name)) && !this['on_' + name]) continue
var prop = this[name]
var ch = name.charCodeAt(0)
// make sure our property-settered values get forwarded
if(ch == 95 && name.charCodeAt(1) == 95 &&
(name.charCodeAt(name.length - 1) != 95 ||
name.charCodeAt(name.length - 2) != 95)){
name = name.slice(2), ch = 0
if(this['on_' + name]) continue // skip signal storage
}
if(ch == 36){ //$
var base = name.slice(1)
if(typeof prop == 'function'){ // execute
if(!comp){
comp = this.__compiles__ = Object.create(this.__compiles__ || null)
}
hash += base + '='+prop.toString()+'\n'
comp[base] = prop // store it
}
else if(prop._ast_){ // its a remote method
var js = this.AST.ToJS
js.new_state()
js.module = prop.module
methods += 'this.' + base + ' = ' + js.expand(prop) + '\n'
for(var k in js.type_methods){
methods = js.type_methods[k] + '\n'+ methods
}
// ok we have to now also add
//cache += base + '=' + prop.source + '\n'
}
}
else if(ch != 95 && !this['_dontproxy_'+name]){ // doesnt start with _
if(this['on_' + name]){ // we haz signal
if(!msg._sigs) msg._sigs = []
msg._sigs.push(name)
msg[name] = this['__' + name].value
}
else
if(prop && typeof prop == 'object'){
if(prop.__proxy__){
if(!msg._refs) msg._refs = []
msg[name] = prop.__proxy__
msg._refs.push(name)
this._propertyProxy(name)
}
else if(prop._t_){ // only copy typed properties
this._propertyProxy(name)
// make a value-forward getter-setter
// check if we are doing a transferable
if(prop._transfer_){
prop._bind_ = this
prop._key_ = name
msg[name] = prop._transfer_(ONE.host, name)
}
else{
msg[name] = prop
}
//var proto_prop = proto[name]
//if(proto_prop !== undefined && (!proto_prop._t_ || proto_prop._t_.name != prop._t_.name)){
// throw new Error('Error, cannot change type from baseclass property '+name+' my type: ' + prop._t_.name)
//}
//else
hash += name + '=' + prop._t_.name + prop._t_.slots + '\n'
}
else if(prop._ast_){ // we found an expression, include it in our compile cache key
var locals = prop.locals
for(var local_name in locals){
// we have to make sure the right thing goes in
var local_val = locals[local_name]
if(local_val && local_val.__proxy__){
msg[local_name] = local_val.__proxy__
if(!msg._refs) msg._refs = []
msg._refs.push(local_name)
}
else if(typeof prop != 'object' || (prop && prop._t_)){
msg[local_name] = local_val
}
}
// Todo: do storing context values here so we can cache compiles
// make a recompile-triggering getter-setter
this._propertyProxy(name)
hash += name + '=' + prop.source + '\n'
}
}
else if(typeof prop != 'function'){
// make a value-forward getter-setter
this._propertyProxy(name)
var proto_prop = proto[name]
if(proto_prop && proto_prop._ast_)
hash += name + '=#\n'
msg[name] = prop
}
}
}
this.__compilehash__ = hash
// we do have to
// only compile things if we are an instance
if(!this.hasOwnProperty('__class__')){
// lets first check if we actually need to compile by comparing
// our __compilehash__ with our prototype chain
// ok so what do we need
if( hash && ((this.__compilehash__ && hash !== this.__compilehash__) ||
hash !== proto.__compilehash__ || !proto.__compiled__)){
this.proxy_refs = Object.create(null)
// WARNING. we might need more strict rules on compiler cache, but lets try it
var code = ONE.host.proxify_cache[hash]
if(code === undefined){
code = ''
var comp = this.__compiles__
for(var name in comp){
var prop = comp[name]
code += prop.call(this) + '\n'
}
code += this._compilePropBinds()
ONE.host.proxify_cache[hash] = code
ONE.host.sendToHost({_type:'proxify_cache', key:hash, value:code})
}
//else console.log('code cache hit!')
// TODO fix compile caching based on hash
if(code){
// ok we have code. now we check if we can place it higher up the prototype chain
var last
while(proto && proto.__compilehash__ == hash){
//console.log('movin it up on ', this.__proxy__)
last = proto
proto = Object.getPrototypeOf(proto)
}
if(last){ // lets store it on last
last.__compiled__ = true
ONE.host.sendToHost({_type:'proxify', _uid:last.__proxy__, _code:code})
}
else{
this.__compiled__ = true
msg._code = code
}
}
}
}
else msg.__class__ = 'Host - '+this.__class__
this.__compilehash__ = hash
msg._code = msg._code?msg._code + methods:methods
// ok we first send our object with codehash
ONE.host.sendToHost(msg)
}
// its a call without return expectation
this.postHost = function(name){
if(arguments.length > 1){
ONE.host.sendToHost({_type:"call", name:name, _uid: this.__proxy__, args:Array.prototype.slice.call(arguments,1)})
}
else ONE.host.sendToHost({_type:"call", name:name, _uid: this.__proxy__})
}
// make an call and return a signal as return value
this.callHost = function(name){
// lets create a pending callback hook
var sig = ONE.Base.Signal.new()
// fetch a call id
var myid
if(ONE.host.call_free.length){
myid = ONE.host.call_free.pop()
}
else{
myid = ONE.host.call_id++
if(myid > 1000) console.log("Runaway call ID detected")
}
ONE.host.call_signals[myid] = sig
if(arguments.length > 1){
ONE.host.sendToHost({_type:"call", name:name, _uid: this.__proxy__, args:Array.prototype.slice.call(arguments,1), callid:myid})
}
else ONE.host.sendToHost({_type:"call", name:name, _uid: this.__proxy__, callid:myid})
return sig
}
this.hideProperties(Object.keys(this))
})
// the baseclass for the host objects
this.Base.HostProxy = this.Base.extend(function(){
this.__proxy_module__ = {local_types:{}}
this.__proxy_cache__ = {}
this.__class__ = 'HostProxy'
this._getsetSig = function(name, worker){
var store = '__' + name
this[store] = this[name]
this.defineProperty(name, {
get:function(){
return this[store]
},
set:function(v){
this[store] = v
// lets forward this
}
})
}
this._bindProp = function(obj, prop, worker){
// what does that look like?
var bind_store = '_bind_' + prop
var store = '__' + prop
if(Array.isArray(obj)){ // we are a late bind
obj.push(this, null, prop)
return
}
if(obj !== this){
if(obj[bind_store]){
obj[bind_store].push(this)
}
else{
obj[bind_store] = [this]
}
}
if(!obj.__lookupSetter__(prop)){
// store old value
obj[store] = obj[prop]
if(typeof obj !='object') throw new Error('not object')
Object.defineProperty(obj, prop, {
get:function(){
return this[store]
},
set:function(v){
var old = this[store]
if(old !== v){ // only forward on change
this[store] = v
var arr = this[bind_store]
if(arr) for(var i = 0, l = arr.length; i < l; i++){
var node = arr[i]
if(node.flagDirty) node.flagDirty()
}
}
if(worker){
var obj = ONE.proxyDataEscape(v) || v
var msg = {_type:'signal', _uid:this.__proxy__, name:prop, value:obj}
//console.log('sig',msg)
worker.sendToWorker(msg)
}
}
})
}
}
this._unbindProp = function(obj, prop){
if(!obj) return
var bind_store = '_bind_' + prop
var arr = obj[bind_store]
var i
if(!Array.isArray(arr) || (i = arr.indexOf(this)) == -1){
console.log('Unbind property error ' + prop, this.__proxy__)
//throw new Error('unbind')
return
}
arr.splice(i, 1)
}
this.hasBinds = function(prop){
var arr = this['_bind_' + prop]
if(!arr || !arr.length) return false
return true
}
this._initFrom = function(msg, worker, isupdate){
var msg_uid = this.__proxy__ = msg._uid
if(isupdate){
if(this._cleanup) this._cleanup()
if(this._deinitBinds) this._deinitBinds()
}
// copy stuff from msg
for(var k in msg){
if(k.charCodeAt(0)!= 95){
// store it
this[k] = msg[k]
}
}
// define signal forwards
var sigs = msg._sigs
if(sigs){
for(var i = 0, l = sigs.length; i < l; i++){
this._bindProp(this, sigs[i], worker)
}
}
var refs = msg._refs
if(refs){
for(var i = 0, l = refs.length; i < l; i++){
var name = refs[i]
var uid = this[name]
if(typeof uid == 'number'){ // we need to resolve it
var obj = worker.proxy_obj[uid]
if(obj && !Array.isArray(obj)) this[name] = obj
else{
// make late resolve array
var arr = obj || (worker.proxy_obj[uid] = [])
arr.push(this, name)
this[name] = arr
}
}
}
}
if(msg._code){
// do some caching
try{
var fn = this.__proxy_cache__[msg._code] ||
(this.__proxy_cache__[msg._code] = Function('__module__', msg._code))
// execute on self to populate class
fn.call(this, this.__proxy_module__)
}
catch(e){
console.log(msg._code)
}
}
// call init
if(msg.__class__) this.__class__ = msg.__class__
if(!this.hasOwnProperty('__class__')){
if(!isupdate){
if(this.prestructor) this.prestructor()
if(this.constructor) this.constructor()
}
if(this._initBinds) this._initBinds()
}
}
})
}
// recursively walk js datastructure and escape __proxy__ object refs
ONE.proxyDataEscape = function(data){
// how do we store refs? {__proxy__}
if(Array.isArray(data)){
var out = []
for(var k = 0; k < data.length;k++){
var obj = ONE.proxyDataEscape(data[k])
if(obj) data[k] = obj
}
}
else if(data && typeof data == 'object'){
if(data.__proxy__){
return {__proxy__:data.__proxy__}
}
for(var k in data){
var obj = ONE.proxyDataEscape(data[k])
if(obj) data[k] = obj
}
}
}
// recursively walk js datastructure and unescape __proxy__ object refs
ONE.proxyDataUnescape = function(data, proxy_lut){
// how do we store refs? {__proxy__}
if(Array.isArray(data)){
var out = []
for(var k = 0; k < data.length; k++){
var obj = ONE.proxyDataUnescape(data[k], proxy_lut)
if(obj) data[k] = obj
}
}
else if(data && typeof data == 'object'){
if(data.__proxy__){
return proxy_lut[data.__proxy__]
}
for(var k in data){
var obj = ONE.proxyDataUnescape(data[k], proxy_lut)
if(obj) data[k] = obj
}
}
}
ONE._createWorker = function(){
var dt = Date.now()
var source =
'\nONE = {}' +
'\nvar Assert'+
'\nONE.init = ' + ONE.init.toString() +
'\nONE.init_ast = ' + ONE.init_ast.toString() +
'\nONE.base_ = ' + ONE.base_.toString() +
'\nONE.proxy_ = ' + ONE.proxy_.toString() +
'\nONE.ast_ = ' + ONE.ast_.toString() +
'\nONE.zlib_ = ' + (ONE.zlib_?ONE.zlib_.toString():'undefined') +
'\nONE.genjs_ = ' + ONE.genjs_.toString() +
'\nONE.genjs_compat_ = ' + ONE.genjs_compat_.toString() +
'\nONE.color_ = ' + ONE.color_.toString() +
'\nONE.parser_strict_ = ' + ONE.parser_strict_.toString() +
'\nONE.worker_boot_ = ' + ONE.worker_boot_.toString() +
'\nONE.proxyDataUnescape = ' + ONE.proxyDataUnescape.toString() +
'\nONE.origin = "'+window.location.origin+'"'+
'\nONE.compress_cache = ' +ONE.compress_cache+
'\nONE.worker_boot_(self)'
var blob = new Blob([source], { type: "text/javascript" })
this._worker_url = URL.createObjectURL(blob)
var worker = new Worker(this._worker_url)
worker.source = source
return worker
}
ONE.origin = window.location.origin
// Bootstrap code for the browser, started at the bottom of the file
ONE.browser_boot_ = function(){
var worker
// fake worker for debugging
if(ONE.fake_worker){
worker = {
postMessage: function(msg){
//setTimeout(function(){
host.onmessage({data:msg})
//},0)
},
onmessage:function(){}
}
var host = {
postMessage: function(msg){
setTimeout(function(){
worker.onmessage({data:msg})
},0)
},
onmessage: function(){}
}
ONE.worker_boot_(host)
}
else worker = ONE._createWorker()
worker.proxy_cache = {}
worker.proxy_obj = {}
var dt = 0
worker.msg_start = 0
worker.msg_queue = []
worker.msgFlush = function(){
var msgs = this.msg_queue
this.msg_start = Date.now()
this.msg_queue = []
this.postMessage(msgs)
}.bind(worker)
worker.sendToWorker = function(msg){
var now = Date.now()
if(this.msg_queue.length && now - this.msg_start > 10){ // make sure we chunk every 20ms for parallelisation
this.msg_queue.push(msg)
this.msgFlush()
}
else{
if(this.msg_queue.push(msg) == 1){
this.msg_start = now
setTimeout(this.msgFlush, 0)
}
}
}
var proxy_module = {
worker:worker
}
window.onkeypress = function(event){
if(event.altKey && event.ctrlKey && event.keyCode == 18){
cache_db.reset()
location.reload()
}
}
// lamo hash. why doesnt js have a really good one built in hmm?
function string_hash(str){
if(typeof str !== 'string') return 0
var hash = 5381,
i = str.length
while(i) hash = (hash * 33) ^ str.charCodeAt(--i)
return hash >>> 0
}
// our module cache database object
var cache_db = {
storage: localStorage,
total_storage: 0,
modules:{},
source:{},
hashes:{},
proxify_hash:'',
reset:function(){
this.storage.clear()
},
init:function(callback){
callback()
},
write_module:function(key, value){
try{
this.storage.setItem(key, value)
this.total_storage += key.length + value.length
}
catch(e){
console.log("clearing storage write_module")
this.storage.clear()
}
},
check_module:function(module_name, source_code, callback){
this.source[module_name] = source_code
this.hashes[module_name] = string_hash(source_code)
if(ONE.ignore_cache){
worker.sendToWorker({_type:'parse', module_name:module_name, value:source_code})
return callback()
}
try{
var data = this.storage.getItem(source_code)
}
catch(e){}
if(!data){
worker.sendToWorker({_type:'parse', module_name:module_name, value:source_code})
return callback()
}
try{
if(ONE.compress_cache) data = ONE.inflate(data, {to:'string'})
else if(data.charCodeAt(0) == 120 && data.charCodeAt(1) == 156) throw 1
this.modules[module_name] = data
}
catch(e){
worker.sendToWorker({_type:'parse', module_name:module_name, value:source_code})
return callback()
}
return callback()
},
get_proxify:function(callback){
if(ONE.ignore_cache){
return callback({})
}
try{
var num = 0
var key
var cache = {}
while(key = this.storage.getItem(this.proxify_hash + 'K' + num)){
if(ONE.compress_cache) key = ONE.inflate(key, {to:'string'})
else if(key.charCodeAt(0) == 120 && key.charCodeAt(1) == 156) throw 1
var value = this.storage.getItem(this.proxify_hash + 'V' + num)
if(value !== undefined){
if(ONE.compress_cache) value = ONE.inflate(value, {to:'string'})
else if(value.charCodeAt(0) == 120 && value.charCodeAt(1) == 156) throw 1
cache[key] = value
}
num++
}
return callback(cache)
}
catch(e){
console.log("clearing storage get_proxify")
localStorage.clear()
callback({})