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



215
216
217
# File 'lib/twostroke/runtime/vm_frame.rb', line 215

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

#add(arg) ⇒ Object



316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/twostroke/runtime/vm_frame.rb', line 316

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



353
354
355
356
357
# File 'lib/twostroke/runtime/vm_frame.rb', line 353

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



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

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

#bnot(arg) ⇒ Object



371
372
373
374
# File 'lib/twostroke/runtime/vm_frame.rb', line 371

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

#call(arg) ⇒ Object



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

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



421
422
423
# File 'lib/twostroke/runtime/vm_frame.rb', line 421

def callee(arg)
  stack.push @callee
end

#close(arg) ⇒ Object



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

def close(arg)
  name = vm.bytecode[arg].select { |ins,arg| ins == :".name" }.map { |ins,arg| arg }.first
  arguments = vm.bytecode[arg].select { |ins,arg| ins == :".arg" }.map(&:last).map(&:to_s)
  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



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

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

#delete(arg) ⇒ Object



156
157
158
159
# File 'lib/twostroke/runtime/vm_frame.rb', line 156

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

#deleteg(arg) ⇒ Object



151
152
153
154
# File 'lib/twostroke/runtime/vm_frame.rb', line 151

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

#deleteindex(arg) ⇒ Object



161
162
163
164
165
166
# File 'lib/twostroke/runtime/vm_frame.rb', line 161

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



341
342
343
344
345
# File 'lib/twostroke/runtime/vm_frame.rb', line 341

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



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

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

#endfinally(arg) ⇒ Object



474
475
476
# File 'lib/twostroke/runtime/vm_frame.rb', line 474

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

#enum(arg) ⇒ Object



174
175
176
177
178
179
# File 'lib/twostroke/runtime/vm_frame.rb', line 174

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



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

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

#eq(arg) ⇒ Object



219
220
221
222
223
# File 'lib/twostroke/runtime/vm_frame.rb', line 219

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
54
55
56
57
58
# 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]
    st = @stack.size
    @ip += 1
    if respond_to? ins
      if @exception = catch(:exception) { public_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
    else
      error! "unknown instruction #{ins}"
    end
  end
  
  stack.last
end

#false(arg) ⇒ Object



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

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

#gt(arg) ⇒ Object



391
392
393
# File 'lib/twostroke/runtime/vm_frame.rb', line 391

def gt(arg)
  comparison_oper :>
end

#gte(arg) ⇒ Object



395
396
397
# File 'lib/twostroke/runtime/vm_frame.rb', line 395

def gte(arg)
  comparison_oper :>=
end

#in(arg) ⇒ Object



168
169
170
171
172
# File 'lib/twostroke/runtime/vm_frame.rb', line 168

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



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

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

#index(arg) ⇒ Object



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

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



407
408
409
410
411
# File 'lib/twostroke/runtime/vm_frame.rb', line 407

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

#jiee(arg) ⇒ Object



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

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

#jif(arg) ⇒ Object



247
248
249
250
251
# File 'lib/twostroke/runtime/vm_frame.rb', line 247

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

#jit(arg) ⇒ Object



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

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

#jmp(arg) ⇒ Object



243
244
245
# File 'lib/twostroke/runtime/vm_frame.rb', line 243

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

#lt(arg) ⇒ Object



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

def lt(arg)
  comparison_oper :<
end

#lte(arg) ⇒ Object



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

def lte(arg)
  comparison_oper :<=
end

#member(arg) ⇒ Object



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

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

#mod(arg) ⇒ Object



347
348
349
350
351
# File 'lib/twostroke/runtime/vm_frame.rb', line 347

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



335
336
337
338
339
# File 'lib/twostroke/runtime/vm_frame.rb', line 335

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



433
434
435
436
437
438
439
440
# File 'lib/twostroke/runtime/vm_frame.rb', line 433

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



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/twostroke/runtime/vm_frame.rb', line 118

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



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

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

#null(arg) ⇒ Object



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

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

#number(arg) ⇒ Object



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

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

#object(arg) ⇒ Object



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

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



359
360
361
362
363
# File 'lib/twostroke/runtime/vm_frame.rb', line 359

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



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

def pop(arg)
  stack.pop
end

#popcatch(arg) ⇒ Object



462
463
464
# File 'lib/twostroke/runtime/vm_frame.rb', line 462

def popcatch(arg)
  catch_stack.pop
end

#popenum(arg) ⇒ Object



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

def popenum(arg)
  @enum_stack.pop
end

#popfinally(arg) ⇒ Object



470
471
472
# File 'lib/twostroke/runtime/vm_frame.rb', line 470

def popfinally(arg)
  finally_stack.pop
end

#popscope(arg) ⇒ Object



446
447
448
# File 'lib/twostroke/runtime/vm_frame.rb', line 446

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

#popsp(arg) ⇒ Object



454
455
456
# File 'lib/twostroke/runtime/vm_frame.rb', line 454

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

#push(arg) ⇒ Object

instructions



87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/twostroke/runtime/vm_frame.rb', line 87

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?(Bignum)
    stack.push Types::Number.new(arg.to_f)
  elsif arg.is_a?(String)
    stack.push Types::String.new(arg)
  else
    error! "bad argument to push instruction"
  end
end

#pushcatch(arg) ⇒ Object



458
459
460
# File 'lib/twostroke/runtime/vm_frame.rb', line 458

def pushcatch(arg)
  catch_stack.push arg
end

#pushfinally(arg) ⇒ Object



466
467
468
# File 'lib/twostroke/runtime/vm_frame.rb', line 466

def pushfinally(arg)
  finally_stack.push arg
end

#pushsp(arg) ⇒ Object



450
451
452
# File 'lib/twostroke/runtime/vm_frame.rb', line 450

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

#regexp(arg) ⇒ Object



294
295
296
# File 'lib/twostroke/runtime/vm_frame.rb', line 294

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

#ret(arg) ⇒ Object



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

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

#sal(arg) ⇒ Object



298
299
300
301
302
# File 'lib/twostroke/runtime/vm_frame.rb', line 298

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



304
305
306
307
308
# File 'lib/twostroke/runtime/vm_frame.rb', line 304

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

#seq(arg) ⇒ Object



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

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

#set(arg) ⇒ Object



196
197
198
# File 'lib/twostroke/runtime/vm_frame.rb', line 196

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

#setindex(arg) ⇒ Object



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

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



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

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



310
311
312
313
314
# File 'lib/twostroke/runtime/vm_frame.rb', line 310

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

#sub(arg) ⇒ Object



329
330
331
332
333
# File 'lib/twostroke/runtime/vm_frame.rb', line 329

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



478
479
480
# File 'lib/twostroke/runtime/vm_frame.rb', line 478

def this(arg)
  stack.push @this
end

#thiscall(arg) ⇒ Object



109
110
111
112
113
114
115
116
# File 'lib/twostroke/runtime/vm_frame.rb', line 109

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



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

def tld(arg)
  stack.push @temp_slot
end

#true(arg) ⇒ Object



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

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

#tst(arg) ⇒ Object



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

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

#typeof(arg) ⇒ Object



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

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



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

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

#with(arg) ⇒ Object



442
443
444
# File 'lib/twostroke/runtime/vm_frame.rb', line 442

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

#xor(arg) ⇒ Object



365
366
367
368
369
# File 'lib/twostroke/runtime/vm_frame.rb', line 365

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