Class: Twostroke::Runtime::VM::Frame

Inherits:
Object
  • Object
show all
Defined in:
lib/twostroke/runtime/vm_frame.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(vm, section, callee = nil) ⇒ Frame

Returns a new instance of Frame.



5
6
7
8
9
10
# File 'lib/twostroke/runtime/vm_frame.rb', line 5

def initialize(vm, section, callee = nil)
  @vm = vm
  @section = section
  @insns = vm.bytecode[section]
  @callee = callee
end

Instance Attribute Details

#catch_stackObject (readonly)

Returns the value of attribute catch_stack.



3
4
5
# File 'lib/twostroke/runtime/vm_frame.rb', line 3

def catch_stack
  @catch_stack
end

#enum_stackObject (readonly)

Returns the value of attribute enum_stack.



3
4
5
# File 'lib/twostroke/runtime/vm_frame.rb', line 3

def enum_stack
  @enum_stack
end

#exceptionObject (readonly)

Returns the value of attribute exception.



3
4
5
# File 'lib/twostroke/runtime/vm_frame.rb', line 3

def exception
  @exception
end

#finally_stackObject (readonly)

Returns the value of attribute finally_stack.



3
4
5
# File 'lib/twostroke/runtime/vm_frame.rb', line 3

def finally_stack
  @finally_stack
end

#insnsObject (readonly)

Returns the value of attribute insns.



3
4
5
# File 'lib/twostroke/runtime/vm_frame.rb', line 3

def insns
  @insns
end

#ipObject (readonly)

Returns the value of attribute ip.



3
4
5
# File 'lib/twostroke/runtime/vm_frame.rb', line 3

def ip
  @ip
end

#scopeObject (readonly)

Returns the value of attribute scope.



3
4
5
# File 'lib/twostroke/runtime/vm_frame.rb', line 3

def scope
  @scope
end

#sp_stackObject (readonly)

Returns the value of attribute sp_stack.



3
4
5
# File 'lib/twostroke/runtime/vm_frame.rb', line 3

def sp_stack
  @sp_stack
end

#stackObject (readonly)

Returns the value of attribute stack.



3
4
5
# File 'lib/twostroke/runtime/vm_frame.rb', line 3

def stack
  @stack
end

#vmObject (readonly)

Returns the value of attribute vm.



3
4
5
# File 'lib/twostroke/runtime/vm_frame.rb', line 3

def vm
  @vm
end

Instance Method Details

#_throw(arg) ⇒ Object



206
207
208
# File 'lib/twostroke/runtime/vm_frame.rb', line 206

def _throw(arg)
  throw :exception, stack.pop
end

#add(arg) ⇒ Object



307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/twostroke/runtime/vm_frame.rb', line 307

def add(arg)
  r = stack.pop
  l = stack.pop
  right = Types.to_primitive r
  left = Types.to_primitive l
  
  if left.is_a?(Types::String) || right.is_a?(Types::String)
    stack.push Types::String.new(Types.to_string(left).string + Types.to_string(right).string)
  else
    stack.push Types::Number.new(Types.to_number(left).number + Types.to_number(right).number)
  end
end

#and(arg) ⇒ Object



344
345
346
347
348
# File 'lib/twostroke/runtime/vm_frame.rb', line 344

def and(arg)
  right = Types.to_int32 stack.pop
  left = Types.to_int32 stack.pop
  stack.push Types::Number.new(left & right)
end

#arguments_objectObject



12
13
14
15
16
17
18
# File 'lib/twostroke/runtime/vm_frame.rb', line 12

def arguments_object
  arguments = Types::Object.new
  @args.each_with_index { |arg,i| arguments.put i.to_s, arg }
  arguments.put "length", Types::Number.new(@args.size)
  arguments.put "callee", @callee
  arguments
end

#array(arg) ⇒ Object



271
272
273
274
275
# File 'lib/twostroke/runtime/vm_frame.rb', line 271

def array(arg)
  args = []
  arg.times { args.unshift stack.pop }
  stack.push Types::Array.new(args)
end

#bnot(arg) ⇒ Object



362
363
364
365
# File 'lib/twostroke/runtime/vm_frame.rb', line 362

def bnot(arg)
  val = Types.to_int32 stack.pop
  stack.push Types::Number.new ~val
end

#call(arg) ⇒ Object



92
93
94
95
96
97
98
# File 'lib/twostroke/runtime/vm_frame.rb', line 92

def call(arg)
  args = []
  arg.times { args.unshift @stack.pop }
  fun = stack.pop
  Lib.throw_type_error "called non callable" unless fun.respond_to?(:call)
  stack.push fun.call(scope, fun.inherits_caller_this ? @this : scope.global_scope.root_object, args)
end

#callee(arg) ⇒ Object



411
412
413
# File 'lib/twostroke/runtime/vm_frame.rb', line 411

def callee(arg)
  stack.push @callee
end

#close(arg) ⇒ Object



404
405
406
407
408
409
# File 'lib/twostroke/runtime/vm_frame.rb', line 404

def close(arg)
  name, arguments = vm.section_name_args arg
  scope = @scope
  fun = Types::Function.new(->(outer_scope, this, args) { VM::Frame.new(vm, arg, fun).execute(scope.close, this, args) }, "...", name || "", arguments)
  stack.push fun
end

#dec(arg) ⇒ Object



258
259
260
# File 'lib/twostroke/runtime/vm_frame.rb', line 258

def dec(arg)
  stack.push Types::Number.new(Types.to_number(stack.pop).number - 1)
end

#delete(arg) ⇒ Object



147
148
149
150
# File 'lib/twostroke/runtime/vm_frame.rb', line 147

def delete(arg)
  Types.to_object(stack.pop).delete arg.to_s
  stack.push Types::Boolean.true
end

#deleteg(arg) ⇒ Object



142
143
144
145
# File 'lib/twostroke/runtime/vm_frame.rb', line 142

def deleteg(arg)
  scope.delete arg
  stack.push Types::Boolean.true
end

#deleteindex(arg) ⇒ Object



152
153
154
155
156
157
# File 'lib/twostroke/runtime/vm_frame.rb', line 152

def deleteindex(arg)
  obj = Types.to_object stack.pop
  idx = Types.to_string stack.pop
  obj.delete idx.string
  stack.push Types::Boolean.true
end

#div(arg) ⇒ Object



332
333
334
335
336
# File 'lib/twostroke/runtime/vm_frame.rb', line 332

def div(arg)
  right = Types.to_number(stack.pop).number
  left = Types.to_number(stack.pop).number
  stack.push Types::Number.new(left / right.to_f)
end

#dup(arg) ⇒ Object



125
126
127
128
# File 'lib/twostroke/runtime/vm_frame.rb', line 125

def dup(arg)
  n = arg || 1
  stack.push *stack[-n..-1]
end

#endfinally(arg) ⇒ Object



464
465
466
# File 'lib/twostroke/runtime/vm_frame.rb', line 464

def endfinally(arg)
  throw :exception, @exception if @exception
end

#enum(arg) ⇒ Object



165
166
167
168
169
170
# File 'lib/twostroke/runtime/vm_frame.rb', line 165

def enum(arg)
  props = []
  obj = stack.pop
  Types.to_object(obj).each_enumerable_property { |p| props.push p } unless obj.is_a?(Types::Null) || obj.is_a?(Types::Undefined)
  @enum_stack.push [props, 0]
end

#enumnext(arg) ⇒ Object



172
173
174
175
176
# File 'lib/twostroke/runtime/vm_frame.rb', line 172

def enumnext(arg)
  enum = @enum_stack.last
  stack.push Types::String.new(enum[0][enum[1]])
  enum[1] += 1
end

#eq(arg) ⇒ Object



210
211
212
213
214
# File 'lib/twostroke/runtime/vm_frame.rb', line 210

def eq(arg)
  b = stack.pop
  a = stack.pop
  stack.push Types::Boolean.new(Types.eq(a, b))
end

#execute(scope, this = nil, args = []) ⇒ Object



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
# File 'lib/twostroke/runtime/vm_frame.rb', line 20

def execute(scope, this = nil, args = [])
  @scope = scope || vm.global_scope
  @stack = []
  @sp_stack = []
  @catch_stack = []
  @finally_stack = []
  @enum_stack = []
  @temp_slot = nil
  @ip = 0
  @return = false
  @this = this || @scope.global_scope.root_object
  @args = args
  if @callee
    # the arguments object is only available within functions
    scope.declare :arguments
    scope.set_var :arguments, arguments_object
  end
  
  until @return
    ins, arg = insns[ip]
    @ip += 1
    if @exception = catch(:exception) { send ins, arg; nil }
#          puts "--> #{Types.to_string(exception).string}  #{@name || "(anonymous function)"}:#{@line}  <#{@section}+#{@ip}>"
      throw :exception, @exception if catch_stack.empty? && finally_stack.empty?
      if catch_stack.any?
        @ip = catch_stack.last
      else
        @ip = finally_stack.last
      end
    end
  end
  
  stack.last
end

#false(arg) ⇒ Object



230
231
232
# File 'lib/twostroke/runtime/vm_frame.rb', line 230

def false(arg)
  stack.push Types::Boolean.false
end

#gt(arg) ⇒ Object



382
383
384
# File 'lib/twostroke/runtime/vm_frame.rb', line 382

def gt(arg)
  comparison_oper :>
end

#gte(arg) ⇒ Object



386
387
388
# File 'lib/twostroke/runtime/vm_frame.rb', line 386

def gte(arg)
  comparison_oper :>=
end

#in(arg) ⇒ Object



159
160
161
162
163
# File 'lib/twostroke/runtime/vm_frame.rb', line 159

def in(arg)
  obj = Types.to_object stack.pop
  idx = Types.to_string stack.pop
  stack.push Types::Boolean.new(obj.has_property idx.string)
end

#inc(arg) ⇒ Object



254
255
256
# File 'lib/twostroke/runtime/vm_frame.rb', line 254

def inc(arg)
  stack.push Types::Number.new(Types.to_number(stack.pop).number + 1)
end

#index(arg) ⇒ Object



266
267
268
269
# File 'lib/twostroke/runtime/vm_frame.rb', line 266

def index(arg)
  index = Types.to_string(stack.pop).string
  stack.push(Types.to_object(stack.pop).get(index) || Types::Undefined.new)
end

#instanceof(arg) ⇒ Object



398
399
400
401
402
# File 'lib/twostroke/runtime/vm_frame.rb', line 398

def instanceof(arg)
  r = stack.pop
  l = stack.pop
  stack.push Types::Boolean.new(r.has_instance l)
end

#jiee(arg) ⇒ Object



178
179
180
181
# File 'lib/twostroke/runtime/vm_frame.rb', line 178

def jiee(arg)
  enum = @enum_stack.last
  @ip = arg if enum[1] >= enum[0].size
end

#jif(arg) ⇒ Object



238
239
240
241
242
# File 'lib/twostroke/runtime/vm_frame.rb', line 238

def jif(arg)
  if Types.is_falsy stack.pop
    @ip = arg.to_i
  end
end

#jit(arg) ⇒ Object



244
245
246
247
248
# File 'lib/twostroke/runtime/vm_frame.rb', line 244

def jit(arg)
  if Types.is_truthy stack.pop
    @ip = arg.to_i
  end
end

#jmp(arg) ⇒ Object



234
235
236
# File 'lib/twostroke/runtime/vm_frame.rb', line 234

def jmp(arg)
  @ip = arg.to_i
end

#lt(arg) ⇒ Object



374
375
376
# File 'lib/twostroke/runtime/vm_frame.rb', line 374

def lt(arg)
  comparison_oper :<
end

#lte(arg) ⇒ Object



378
379
380
# File 'lib/twostroke/runtime/vm_frame.rb', line 378

def lte(arg)
  comparison_oper :<=
end

#member(arg) ⇒ Object



138
139
140
# File 'lib/twostroke/runtime/vm_frame.rb', line 138

def member(arg)
  stack.push Types.to_object(stack.pop).get(arg.to_s)
end

#mod(arg) ⇒ Object



338
339
340
341
342
# File 'lib/twostroke/runtime/vm_frame.rb', line 338

def mod(arg)
  right = Types.to_number(stack.pop).number
  left = Types.to_number(stack.pop).number
  stack.push Types::Number.new(left % right)
end

#mul(arg) ⇒ Object



326
327
328
329
330
# File 'lib/twostroke/runtime/vm_frame.rb', line 326

def mul(arg)
  right = Types.to_number(stack.pop).number
  left = Types.to_number(stack.pop).number
  stack.push Types::Number.new(left * right)
end

#negate(arg) ⇒ Object



423
424
425
426
427
428
429
430
# File 'lib/twostroke/runtime/vm_frame.rb', line 423

def negate(arg)
  n = Types.to_number(stack.pop).number
  if n.zero?
    stack.push Types::Number.new(-n.to_f) # to preserve javascript's 0/-0 semantics
  else
    stack.push Types::Number.new(-n)
  end
end

#newcall(arg) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/twostroke/runtime/vm_frame.rb', line 109

def newcall(arg)
  args = []
  arg.times { args.unshift @stack.pop }
  fun = stack.pop
  Lib.throw_type_error "called non callable" unless fun.respond_to?(:call)
  obj = Types::Object.new
  obj.construct prototype: fun.get("prototype"), _class: fun do
    retn = fun.call(scope, obj, args)
    if retn.is_a?(Types::Undefined)
      stack.push obj
    else
      stack.push retn
    end
  end
end

#not(arg) ⇒ Object



250
251
252
# File 'lib/twostroke/runtime/vm_frame.rb', line 250

def not(arg)
  stack.push Types::Boolean.new(Types.is_falsy(stack.pop))
end

#null(arg) ⇒ Object



222
223
224
# File 'lib/twostroke/runtime/vm_frame.rb', line 222

def null(arg)
  stack.push Types::Null.new
end

#number(arg) ⇒ Object



281
282
283
# File 'lib/twostroke/runtime/vm_frame.rb', line 281

def number(arg)
  stack.push Types.to_number(stack.pop)
end

#object(arg) ⇒ Object



415
416
417
418
419
420
421
# File 'lib/twostroke/runtime/vm_frame.rb', line 415

def object(arg)
  obj = Types::Object.new
  kvs = []
  arg.reverse_each { |a| kvs << [a, stack.pop] }
  kvs.reverse_each { |kv| obj.put kv[0].to_s, kv[1] }
  stack.push obj
end

#or(arg) ⇒ Object



350
351
352
353
354
# File 'lib/twostroke/runtime/vm_frame.rb', line 350

def or(arg)
  right = Types.to_int32 stack.pop
  left = Types.to_int32 stack.pop
  stack.push Types::Number.new(left | right)
end

#pop(arg) ⇒ Object



262
263
264
# File 'lib/twostroke/runtime/vm_frame.rb', line 262

def pop(arg)
  stack.pop
end

#popcatch(arg) ⇒ Object



452
453
454
# File 'lib/twostroke/runtime/vm_frame.rb', line 452

def popcatch(arg)
  catch_stack.pop
end

#popenum(arg) ⇒ Object



183
184
185
# File 'lib/twostroke/runtime/vm_frame.rb', line 183

def popenum(arg)
  @enum_stack.pop
end

#popfinally(arg) ⇒ Object



460
461
462
# File 'lib/twostroke/runtime/vm_frame.rb', line 460

def popfinally(arg)
  finally_stack.pop
end

#popscope(arg) ⇒ Object



436
437
438
# File 'lib/twostroke/runtime/vm_frame.rb', line 436

def popscope(arg)
  @scope = @scope.parent
end

#popsp(arg) ⇒ Object



444
445
446
# File 'lib/twostroke/runtime/vm_frame.rb', line 444

def popsp(arg)
  @stack = stack[0...sp_stack.pop]
end

#push(arg) ⇒ Object

instructions



82
83
84
85
86
87
88
89
90
# File 'lib/twostroke/runtime/vm_frame.rb', line 82

def push(arg)
  if arg.is_a? Symbol
    stack.push scope.get_var(arg)
  elsif arg.is_a?(Fixnum) || arg.is_a?(Float)
    stack.push Types::Number.new(arg)
  elsif arg.is_a?(String)
    stack.push Types::String.new(arg)
  end
end

#pushcatch(arg) ⇒ Object



448
449
450
# File 'lib/twostroke/runtime/vm_frame.rb', line 448

def pushcatch(arg)
  catch_stack.push arg
end

#pushfinally(arg) ⇒ Object



456
457
458
# File 'lib/twostroke/runtime/vm_frame.rb', line 456

def pushfinally(arg)
  finally_stack.push arg
end

#pushsp(arg) ⇒ Object



440
441
442
# File 'lib/twostroke/runtime/vm_frame.rb', line 440

def pushsp(arg)
  sp_stack.push stack.size
end

#regexp(arg) ⇒ Object



285
286
287
# File 'lib/twostroke/runtime/vm_frame.rb', line 285

def regexp(arg)
  stack.push Types::RegExp.new(*arg)
end

#ret(arg) ⇒ Object



198
199
200
201
202
203
204
# File 'lib/twostroke/runtime/vm_frame.rb', line 198

def ret(arg)
  if finally_stack.empty?
    @return = true
  else
    @ip = finally_stack.last
  end
end

#sal(arg) ⇒ Object



289
290
291
292
293
# File 'lib/twostroke/runtime/vm_frame.rb', line 289

def sal(arg)
  r = Types.to_uint32(stack.pop) & 31
  l = Types.to_int32 stack.pop
  stack.push Types::Number.new(l << r)
end

#sar(arg) ⇒ Object



301
302
303
304
305
# File 'lib/twostroke/runtime/vm_frame.rb', line 301

def sar(arg)
  r = Types.to_uint32(stack.pop) & 31
  l = Types.to_uint32 stack.pop
  stack.push Types::Number.new(l >> r)
end

#seq(arg) ⇒ Object



216
217
218
219
220
# File 'lib/twostroke/runtime/vm_frame.rb', line 216

def seq(arg)
  b = stack.pop
  a = stack.pop
  stack.push Types::Boolean.new(Types.seq(a, b))
end

#set(arg) ⇒ Object



187
188
189
# File 'lib/twostroke/runtime/vm_frame.rb', line 187

def set(arg)
  scope.set_var arg, stack.last
end

#setindex(arg) ⇒ Object



367
368
369
370
371
372
# File 'lib/twostroke/runtime/vm_frame.rb', line 367

def setindex(arg)
  val = stack.pop
  index = Types.to_string(stack.pop).string
  Types.to_object(stack.pop).put index, val
  stack.push val
end

#setprop(arg) ⇒ Object



191
192
193
194
195
196
# File 'lib/twostroke/runtime/vm_frame.rb', line 191

def setprop(arg)
  val = stack.pop
  obj = Types.to_object(stack.pop)
  obj.put arg.to_s, val
  stack.push val
end

#slr(arg) ⇒ Object



295
296
297
298
299
# File 'lib/twostroke/runtime/vm_frame.rb', line 295

def slr(arg)
  r = Types.to_uint32(stack.pop) & 31
  l = Types.to_int32 stack.pop
  stack.push Types::Number.new(l >> r)
end

#sub(arg) ⇒ Object



320
321
322
323
324
# File 'lib/twostroke/runtime/vm_frame.rb', line 320

def sub(arg)
  right = Types.to_number(stack.pop).number
  left = Types.to_number(stack.pop).number
  stack.push Types::Number.new(left - right)
end

#this(arg) ⇒ Object



468
469
470
# File 'lib/twostroke/runtime/vm_frame.rb', line 468

def this(arg)
  stack.push @this
end

#thiscall(arg) ⇒ Object



100
101
102
103
104
105
106
107
# File 'lib/twostroke/runtime/vm_frame.rb', line 100

def thiscall(arg)
  args = []
  arg.times { args.unshift stack.pop }
  fun = stack.pop
  Lib.throw_type_error "called non callable" unless fun.respond_to?(:call)
  this_arg = Types.to_object stack.pop
  stack.push fun.call(scope, fun.inherits_caller_this ? @this : this_arg, args)
end

#tld(arg) ⇒ Object



134
135
136
# File 'lib/twostroke/runtime/vm_frame.rb', line 134

def tld(arg)
  stack.push @temp_slot
end

#true(arg) ⇒ Object



226
227
228
# File 'lib/twostroke/runtime/vm_frame.rb', line 226

def true(arg)
  stack.push Types::Boolean.true
end

#tst(arg) ⇒ Object



130
131
132
# File 'lib/twostroke/runtime/vm_frame.rb', line 130

def tst(arg)
  @temp_slot = stack.pop
end

#typeof(arg) ⇒ Object



390
391
392
393
394
395
396
# File 'lib/twostroke/runtime/vm_frame.rb', line 390

def typeof(arg)
  if arg
    stack.push Types::String.new(scope.has_var(arg) ? scope.get_var(arg).typeof : "undefined")
  else
    stack.push Types::String.new(stack.pop.typeof)
  end
end

#undefined(arg) ⇒ Object



277
278
279
# File 'lib/twostroke/runtime/vm_frame.rb', line 277

def undefined(arg)
  stack.push Types::Undefined.new
end

#with(arg) ⇒ Object



432
433
434
# File 'lib/twostroke/runtime/vm_frame.rb', line 432

def with(arg)
  @scope = ObjectScope.new stack.pop, @scope
end

#xor(arg) ⇒ Object



356
357
358
359
360
# File 'lib/twostroke/runtime/vm_frame.rb', line 356

def xor(arg)
  right = Types.to_int32 stack.pop
  left = Types.to_int32 stack.pop
  stack.push Types::Number.new(left ^ right)
end