envjs 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/envjsrb +0 -1
- data/lib/envjs/env.js +1240 -24047
- data/lib/envjs/event_loop.js +204 -0
- data/lib/envjs/runtime.rb +123 -45
- data/lib/envjs/static.js +22482 -0
- data/lib/envjs.rb +10 -0
- data/test/unit/iframe.js +1 -1
- data/test/unit/parser.js +2 -1
- metadata +24 -11
- data/test/call-load-test.js.smp +0 -14
@@ -0,0 +1,204 @@
|
|
1
|
+
(function(){
|
2
|
+
if ($master.eventLoop) {
|
3
|
+
throw new Error("event loop multiply defined");
|
4
|
+
}
|
5
|
+
var el = $master.eventLoop = {};
|
6
|
+
|
7
|
+
var $env = {};
|
8
|
+
$env.sync = function(f){return f;};
|
9
|
+
$env.error = function(s){
|
10
|
+
print("timer error: "+s);
|
11
|
+
if (s.stack) {
|
12
|
+
print("timer error: "+s.stack);
|
13
|
+
}
|
14
|
+
};
|
15
|
+
$env.sleep = function(t){ Ruby.sleep(t/1000.); };
|
16
|
+
|
17
|
+
var $timers = [];
|
18
|
+
var $event_loop_running = false;
|
19
|
+
$timers.lock = $env.sync(function(fn){fn();});
|
20
|
+
|
21
|
+
var $timer = function(w, fn, interval){
|
22
|
+
this.w = w;
|
23
|
+
this.fn = fn;
|
24
|
+
this.interval = interval;
|
25
|
+
this.at = Date.now() + interval;
|
26
|
+
this.running = false; // allows for calling wait() from callbacks
|
27
|
+
};
|
28
|
+
|
29
|
+
var convert_time = function(time) {
|
30
|
+
time = time*1;
|
31
|
+
if ( isNaN(time) || time < 0 ) {
|
32
|
+
time = 0;
|
33
|
+
}
|
34
|
+
// html5 says this should be at least 4, but the parser is using a setTimeout for the SAX stuff
|
35
|
+
// which messes up the world
|
36
|
+
var min = /* 4 */ 0;
|
37
|
+
if ( $event_loop_running && time < min ) {
|
38
|
+
time = min;
|
39
|
+
}
|
40
|
+
return time;
|
41
|
+
};
|
42
|
+
|
43
|
+
var enter_and_exec = function(w,fn) {
|
44
|
+
|
45
|
+
};
|
46
|
+
|
47
|
+
el.setTimeout = function(w, fn, time){
|
48
|
+
var num;
|
49
|
+
time = convert_time(time);
|
50
|
+
$timers.lock(function(){
|
51
|
+
num = $timers.length+1;
|
52
|
+
var tfn;
|
53
|
+
if (typeof fn == 'string') {
|
54
|
+
tfn = function() {
|
55
|
+
try {
|
56
|
+
eval(fn);
|
57
|
+
} catch (e) {
|
58
|
+
$env.error(e);
|
59
|
+
} finally {
|
60
|
+
el.clearInterval(num);
|
61
|
+
}
|
62
|
+
};
|
63
|
+
} else {
|
64
|
+
tfn = function() {
|
65
|
+
try {
|
66
|
+
fn();
|
67
|
+
} catch (e) {
|
68
|
+
$env.error(e);
|
69
|
+
} finally {
|
70
|
+
el.clearInterval(num);
|
71
|
+
}
|
72
|
+
};
|
73
|
+
}
|
74
|
+
$timers[num] = new $timer(w, tfn, time);
|
75
|
+
});
|
76
|
+
return num;
|
77
|
+
};
|
78
|
+
|
79
|
+
el.setInterval = function(w, fn, time){
|
80
|
+
time = convert_time(time);
|
81
|
+
if ( time < 10 ) {
|
82
|
+
time = 10;
|
83
|
+
}
|
84
|
+
if (typeof fn == 'string') {
|
85
|
+
var fnstr = fn;
|
86
|
+
fn = function() {
|
87
|
+
eval(fnstr);
|
88
|
+
};
|
89
|
+
}
|
90
|
+
var num;
|
91
|
+
$timers.lock(function(){
|
92
|
+
num = $timers.length+1;
|
93
|
+
$timers[num] = new $timer(w, fn, time);
|
94
|
+
});
|
95
|
+
return num;
|
96
|
+
};
|
97
|
+
|
98
|
+
el.clear = el.clearInterval = el.clearTimeout = function(num){
|
99
|
+
//$log("clearing interval "+num);
|
100
|
+
$timers.lock(function(){
|
101
|
+
if ( $timers[num] ) {
|
102
|
+
delete $timers[num];
|
103
|
+
}
|
104
|
+
});
|
105
|
+
};
|
106
|
+
|
107
|
+
// wait === null/undefined: execute any timers as they fire, waiting until there are none left
|
108
|
+
// wait(n) (n > 0): execute any timers as they fire until there are none left waiting at least n ms
|
109
|
+
// but no more, even if there are future events/current threads
|
110
|
+
// wait(0): execute any immediately runnable timers and return
|
111
|
+
// wait(-n): keep sleeping until the next event is more than n ms in the future
|
112
|
+
|
113
|
+
// FIX: make a priority queue ...
|
114
|
+
|
115
|
+
el.wait = function(wait) {
|
116
|
+
// print("wait",wait,$event_loop_running);
|
117
|
+
var fired = false;
|
118
|
+
var delta_wait;
|
119
|
+
if (wait < 0) {
|
120
|
+
delta_wait = -wait;
|
121
|
+
wait = 0;
|
122
|
+
}
|
123
|
+
var start = Date.now();
|
124
|
+
var old_loop_running = $event_loop_running;
|
125
|
+
$event_loop_running = true;
|
126
|
+
if (wait !== 0 && wait !== null && wait !== undefined){
|
127
|
+
wait += Date.now();
|
128
|
+
}
|
129
|
+
for (;;) {
|
130
|
+
var earliest;
|
131
|
+
$timers.lock(function(){
|
132
|
+
earliest = undefined;
|
133
|
+
for(var i in $timers){
|
134
|
+
if( isNaN(i*0) ) {
|
135
|
+
continue;
|
136
|
+
}
|
137
|
+
var timer = $timers[i];
|
138
|
+
if( !timer.running && ( !earliest || timer.at < earliest.at) ) {
|
139
|
+
earliest = timer;
|
140
|
+
}
|
141
|
+
}
|
142
|
+
});
|
143
|
+
var sleep = earliest && earliest.at - Date.now();
|
144
|
+
if ( earliest && sleep <= 0 ) {
|
145
|
+
var f = earliest.fn;
|
146
|
+
var previous = $master.first_script_window;
|
147
|
+
fired = true;
|
148
|
+
try {
|
149
|
+
earliest.running = true;
|
150
|
+
$master.first_script_window = earliest.w;
|
151
|
+
f();
|
152
|
+
} catch (e) {
|
153
|
+
$env.error(e);
|
154
|
+
} finally {
|
155
|
+
earliest.running = false;
|
156
|
+
$master.first_script_window = previous;
|
157
|
+
}
|
158
|
+
var goal = earliest.at + earliest.interval;
|
159
|
+
var now = Date.now();
|
160
|
+
if ( goal < now ) {
|
161
|
+
earliest.at = now;
|
162
|
+
} else {
|
163
|
+
earliest.at = goal;
|
164
|
+
}
|
165
|
+
continue;
|
166
|
+
}
|
167
|
+
|
168
|
+
// bunch of subtle cases here ...
|
169
|
+
if ( !earliest ) {
|
170
|
+
// no events in the queue (but maybe XHR will bring in events, so ...
|
171
|
+
if ( !wait || wait < Date.now() ) {
|
172
|
+
// Loop ends if there are no events and a wait hasn't been requested or has expired
|
173
|
+
break;
|
174
|
+
}
|
175
|
+
// no events, but a wait requested: fall through to sleep
|
176
|
+
} else {
|
177
|
+
// there are events in the queue, but they aren't firable now
|
178
|
+
// print(delta_wait,sleep);
|
179
|
+
if ( delta_wait && sleep <= delta_wait ) {
|
180
|
+
// if they will happen within the next delta, fall through to sleep
|
181
|
+
} else if ( wait === 0 || ( wait > 0 && wait < Date.now () ) ) {
|
182
|
+
// loop ends even if there are events but the user specifcally asked not to wait too long
|
183
|
+
break;
|
184
|
+
}
|
185
|
+
// there are events and the user wants to wait: fall through to sleep
|
186
|
+
}
|
187
|
+
|
188
|
+
// Related to ajax threads ... hopefully can go away ..
|
189
|
+
var interval = el.wait.interval || 100;
|
190
|
+
if ( !sleep || sleep > interval ) {
|
191
|
+
sleep = interval;
|
192
|
+
}
|
193
|
+
// the parser sets the timeout at 1 ... the chances of us getting back through this loop in that amount of time
|
194
|
+
// are small, no need to both the driver for something so small; just spin
|
195
|
+
if (sleep>1) {
|
196
|
+
$env.sleep(sleep);
|
197
|
+
}
|
198
|
+
}
|
199
|
+
$event_loop_running = old_loop_running;
|
200
|
+
// print("unwait",earliest,sleep);
|
201
|
+
return [ fired, earliest && sleep ];
|
202
|
+
};
|
203
|
+
|
204
|
+
}());
|
data/lib/envjs/runtime.rb
CHANGED
@@ -11,7 +11,59 @@ module Envjs::Runtime
|
|
11
11
|
def self.extended object
|
12
12
|
object.instance_eval do
|
13
13
|
|
14
|
-
|
14
|
+
outer = nil
|
15
|
+
scripts = {}
|
16
|
+
|
17
|
+
master = global["$master"] = evaluate("new Object", nil, nil, nil, global)
|
18
|
+
|
19
|
+
( class << self; self; end ).send :define_method, :master do
|
20
|
+
master
|
21
|
+
end
|
22
|
+
|
23
|
+
( class << self; self; end ).send :define_method, :evaluate do |*args|
|
24
|
+
( script, file, line, global, scope, fn ) = *args
|
25
|
+
scope ||= outer["$inner"]
|
26
|
+
raise "cannot evaluate nil script" if script.nil?
|
27
|
+
raise "cannot evaluate without a scope" if scope.nil?
|
28
|
+
raise "outer given when inner needed" if !scope == global and !scope["isInner"]
|
29
|
+
# print "eval in " + script[0,50].inspect + scope.inspect + " " + ( scope ? scope.isInner.inspect : "none" ) + "\n"
|
30
|
+
global = nil
|
31
|
+
# scope ||= inner
|
32
|
+
if fn
|
33
|
+
compiled_script = scripts[fn]
|
34
|
+
end
|
35
|
+
compiled_script ||= compile(script, file, line, global)
|
36
|
+
raise "hell" if !compiled_script
|
37
|
+
if fn && !scripts[fn]
|
38
|
+
scripts[fn] = compiled_script
|
39
|
+
end
|
40
|
+
save = master["first_script_window"]
|
41
|
+
if false
|
42
|
+
p scope
|
43
|
+
if master["first_script_window"]
|
44
|
+
print "ignored: " + ( scope["location"] ? scope["location"]["href"] : "nil" ) + " using " + ( master["first_script_window"]["location"] ? master["first_script_window"]["location"]["href"] : "nil" ) + "\n"
|
45
|
+
else
|
46
|
+
print "pushing into " + ( scope["location"] ? scope["location"]["href"] : "nil" ) + "\n"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
master["first_script_window"] ||= scope
|
51
|
+
raise "hell" if !master["first_script_window"]["isInner"] && master["first_script_window"] != self.global
|
52
|
+
v = nil
|
53
|
+
begin
|
54
|
+
v = evaluate_compiled_script(compiled_script,scope)
|
55
|
+
# p "pe", v, compiled_script, scope
|
56
|
+
rescue Exception => e
|
57
|
+
# p "oopsrt", e
|
58
|
+
raise e
|
59
|
+
ensure
|
60
|
+
master["first_script_window"] = save
|
61
|
+
end
|
62
|
+
# print "done\n"
|
63
|
+
v
|
64
|
+
end
|
65
|
+
|
66
|
+
evaluate( <<'EOJS', nil, nil, nil, global )
|
15
67
|
print = function() {
|
16
68
|
var l = arguments.length
|
17
69
|
for( var i = 0; i < l; i++ ) {
|
@@ -33,7 +85,7 @@ print = function() {
|
|
33
85
|
};
|
34
86
|
EOJS
|
35
87
|
|
36
|
-
evaluate <<'EOJS'
|
88
|
+
evaluate <<'EOJS', nil, nil, nil, global
|
37
89
|
debug = function() {
|
38
90
|
var l = arguments.length
|
39
91
|
for( var i = 0; i < l; i++ ) {
|
@@ -54,7 +106,7 @@ debug = function() {
|
|
54
106
|
};
|
55
107
|
EOJS
|
56
108
|
|
57
|
-
evaluate <<'EOJS'
|
109
|
+
evaluate <<'EOJS', nil, nil, nil, global
|
58
110
|
puts = function() {
|
59
111
|
var l = arguments.length
|
60
112
|
for( var i = 0; i < l; i++ ) {
|
@@ -72,10 +124,15 @@ puts = function() {
|
|
72
124
|
};
|
73
125
|
EOJS
|
74
126
|
|
75
|
-
master = global["$master"] = evaluate("new Object")
|
76
127
|
master["runtime"] = self
|
77
|
-
|
128
|
+
window_index = -1
|
129
|
+
master["next_window_index"] = lambda { window_index += 1 }
|
130
|
+
master.symbols = [ "Johnson", "Ruby", "print", "debug", "puts", "load", "reload", "whichInterpreter", "multiwindow", "seal" ]
|
78
131
|
master.symbols.each { |symbol| master[symbol] = global[symbol] }
|
132
|
+
master["seal"] = lambda do |*args|
|
133
|
+
object, deep = *args
|
134
|
+
seal object, deep
|
135
|
+
end
|
79
136
|
|
80
137
|
master.whichInterpreter = "Johnson"
|
81
138
|
|
@@ -180,8 +237,10 @@ EOJS
|
|
180
237
|
|
181
238
|
master.load = lambda { |*files|
|
182
239
|
if files.length == 2 && !(String === files[1])
|
240
|
+
# now = Time.now
|
183
241
|
f = files[0]
|
184
242
|
w = files[1]
|
243
|
+
# p "load", f, w
|
185
244
|
|
186
245
|
# Hmmm ...
|
187
246
|
uri = URI.parse f
|
@@ -206,6 +265,7 @@ EOJS
|
|
206
265
|
loc = nil
|
207
266
|
add_dep.call w, f
|
208
267
|
evaluate(v, f, 1, w, w, f)
|
268
|
+
# print "load #{uri_s}: #{Time.now-now}\n"
|
209
269
|
else
|
210
270
|
load *files
|
211
271
|
end
|
@@ -265,63 +325,43 @@ EOJS
|
|
265
325
|
# create an proto window object and proxy
|
266
326
|
|
267
327
|
outer = new_split_global_outer
|
268
|
-
|
328
|
+
inner = new_split_global_inner( outer )
|
269
329
|
|
270
330
|
master.symbols.each do |symbol|
|
271
|
-
|
331
|
+
inner[symbol] = master[symbol]
|
272
332
|
end
|
273
333
|
|
274
|
-
|
334
|
+
inner["$inner"] = inner
|
335
|
+
inner["$master"] = master
|
336
|
+
inner["$options"] = evaluate("new Object", nil, nil, nil, inner);
|
337
|
+
inner["$options"].proxy = outer
|
275
338
|
|
276
|
-
|
277
|
-
|
278
|
-
window["$options"] = evaluate("new Object");
|
279
|
-
window["$options"].proxy = outer
|
280
|
-
|
281
|
-
window.evaluate = lambda { |s|
|
282
|
-
return master.evaluate.call(s,window);
|
339
|
+
inner.evaluate = lambda { |s|
|
340
|
+
return master.evaluate.call(s,inner);
|
283
341
|
}
|
284
342
|
|
285
|
-
|
343
|
+
inner.load = lambda { |*files|
|
286
344
|
files.each do |f|
|
287
|
-
master.load.call f,
|
345
|
+
master.load.call f, inner
|
288
346
|
end
|
289
347
|
}
|
290
348
|
|
291
|
-
|
349
|
+
inner.reload = lambda { |*files|
|
292
350
|
files.each do |f|
|
293
|
-
master.reload.call f,
|
351
|
+
master.reload.call f, inner
|
294
352
|
end
|
295
353
|
}
|
296
354
|
|
297
355
|
( class << self; self; end ).send :define_method, :wait do
|
298
356
|
master["finalize"] && master.finalize.call
|
299
|
-
master.
|
357
|
+
master.eventLoop && master.eventLoop.wait
|
300
358
|
end
|
301
359
|
|
302
|
-
|
303
|
-
|
304
|
-
( class << self; self; end ).send :define_method, :become_first_script_window do
|
360
|
+
( class << self; self; end ).send :define_method, :_become_first_script_window do
|
305
361
|
# p "heh ++++++++++++++++++++++++++++", inner, master.first_script_window
|
306
362
|
inner = master.first_script_window
|
307
363
|
end
|
308
364
|
|
309
|
-
( class << self; self; end ).send :define_method, :evaluate do |*args|
|
310
|
-
( script, file, line, global, scope, fn ) = *args
|
311
|
-
raise "cannot evaluate nil script" if script.nil?
|
312
|
-
# print "eval in " + script[0,50].inspect + scope.inspect + " " + ( scope ? scope.isInner.inspect : "none" ) + "\n"
|
313
|
-
global = nil
|
314
|
-
scope ||= inner
|
315
|
-
if fn
|
316
|
-
compiled_script = scripts[fn]
|
317
|
-
end
|
318
|
-
compiled_script ||= compile(script, file, line, global)
|
319
|
-
if fn && !scripts[fn]
|
320
|
-
scripts[fn] = compiled_script
|
321
|
-
end
|
322
|
-
evaluate_compiled_script(compiled_script,scope)
|
323
|
-
end
|
324
|
-
|
325
365
|
( class << self; self; end ).send :define_method, :reevaluate do |*args|
|
326
366
|
( script, file, line, global, scope, fn ) = *args
|
327
367
|
raise "cannot evaluate nil script" if script.nil?
|
@@ -332,20 +372,58 @@ EOJS
|
|
332
372
|
if fn
|
333
373
|
scripts[fn] = compiled_script
|
334
374
|
end
|
335
|
-
|
375
|
+
begin
|
376
|
+
evaluate_compiled_script(compiled_script,scope)
|
377
|
+
rescue Exception => e
|
378
|
+
p e
|
379
|
+
raise e
|
380
|
+
end
|
336
381
|
end
|
337
382
|
|
338
|
-
@envjs = inner
|
339
|
-
|
340
383
|
( class << self; self; end ).send :define_method, :"[]" do |key|
|
341
|
-
key == "this" && evaluate("this") || @envjs[key]
|
384
|
+
# key == "this" && evaluate("this", nil, nil, nil, inner) || @envjs[key]
|
385
|
+
key == "this" && outer || outer[key]
|
342
386
|
end
|
343
387
|
|
344
388
|
( class << self; self; end ).send :define_method, :"[]=" do |k,v|
|
345
|
-
|
389
|
+
# inner[k] = v
|
390
|
+
outer[k] = v
|
391
|
+
end
|
392
|
+
|
393
|
+
master.load.call Envjs::EVENT_LOOP, global
|
394
|
+
|
395
|
+
if false
|
396
|
+
static_outer = new_split_global_outer
|
397
|
+
static_inner = new_split_global_inner static_outer
|
398
|
+
|
399
|
+
master.symbols.each do |symbol|
|
400
|
+
static_inner[symbol] = master[symbol]
|
401
|
+
end
|
402
|
+
|
403
|
+
static_inner["$inner"] = static_inner
|
404
|
+
static_inner["$master"] = master
|
405
|
+
|
406
|
+
master.load.call Envjs::STATIC, static_inner
|
407
|
+
|
408
|
+
master["static"] = static_inner
|
409
|
+
end
|
410
|
+
if true
|
411
|
+
static = new_global
|
412
|
+
|
413
|
+
master.symbols.each do |symbol|
|
414
|
+
static[symbol] = master[symbol]
|
346
415
|
end
|
347
416
|
|
348
|
-
|
417
|
+
static["$master"] = master
|
418
|
+
|
419
|
+
master.load.call Envjs::STATIC, static
|
420
|
+
|
421
|
+
master["static"] = static
|
422
|
+
end
|
423
|
+
|
424
|
+
master.load.call Envjs::ENVJS, inner
|
425
|
+
|
426
|
+
inner = nil
|
349
427
|
end
|
350
428
|
end
|
351
429
|
|