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



185
186
187
# File 'lib/twostroke/runtime/vm_frame.rb', line 185

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

#add(arg) ⇒ Object



286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/twostroke/runtime/vm_frame.rb', line 286

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



323
324
325
326
327
# File 'lib/twostroke/runtime/vm_frame.rb', line 323

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



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

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

#call(arg) ⇒ Object



89
90
91
92
93
94
95
# File 'lib/twostroke/runtime/vm_frame.rb', line 89

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, scope.global_scope.root_object, args)
end

#callee(arg) ⇒ Object



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

def callee(arg)
  stack.push @callee
end

#close(arg) ⇒ Object



378
379
380
381
382
383
384
# File 'lib/twostroke/runtime/vm_frame.rb', line 378

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



237
238
239
# File 'lib/twostroke/runtime/vm_frame.rb', line 237

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

#delete(arg) ⇒ Object



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

def delete(arg)
  Types.to_object(stack.pop).delete arg.to_s
end

#deleteg(arg) ⇒ Object



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

def deleteg(arg)
  scope.global_scope.root_object.delete arg.to_s
end

#div(arg) ⇒ Object



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

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



121
122
123
124
# File 'lib/twostroke/runtime/vm_frame.rb', line 121

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

#enum(arg) ⇒ Object



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

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



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

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

#eq(arg) ⇒ Object



189
190
191
192
193
# File 'lib/twostroke/runtime/vm_frame.rb', line 189

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

def execute(scope, this = nil, args = [])
  @scope = scope || Scope.new(vm.global_scope)
  @stack = []
  @sp_stack = []
  @catch_stack = []
  @finally_stack = []
  @enum_stack = []
  @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 }
        throw :exception, @exception if catch_stack.empty?
        @ip = catch_stack.last
      end
    else
      error! "unknown instruction #{ins}"
    end
  end
  
  stack.last
end

#false(arg) ⇒ Object



209
210
211
# File 'lib/twostroke/runtime/vm_frame.rb', line 209

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

#gt(arg) ⇒ Object



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

def gt(arg)
  comparison_oper :>
end

#gte(arg) ⇒ Object



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

def gte(arg)
  comparison_oper :>=
end

#in(arg) ⇒ Object



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

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



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

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

#index(arg) ⇒ Object



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

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



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

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

#jiee(arg) ⇒ Object



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

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

#jif(arg) ⇒ Object



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

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

#jit(arg) ⇒ Object



223
224
225
226
227
# File 'lib/twostroke/runtime/vm_frame.rb', line 223

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

#jmp(arg) ⇒ Object



213
214
215
# File 'lib/twostroke/runtime/vm_frame.rb', line 213

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

#lt(arg) ⇒ Object



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

def lt(arg)
  comparison_oper :<
end

#lte(arg) ⇒ Object



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

def lte(arg)
  comparison_oper :<=
end

#member(arg) ⇒ Object



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

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

#mod(arg) ⇒ Object



317
318
319
320
321
# File 'lib/twostroke/runtime/vm_frame.rb', line 317

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



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

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



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

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



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/twostroke/runtime/vm_frame.rb', line 105

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



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

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

#null(arg) ⇒ Object



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

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

#number(arg) ⇒ Object



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

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

#object(arg) ⇒ Object



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

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



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

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



241
242
243
# File 'lib/twostroke/runtime/vm_frame.rb', line 241

def pop(arg)
  stack.pop
end

#popcatch(arg) ⇒ Object



427
428
429
# File 'lib/twostroke/runtime/vm_frame.rb', line 427

def popcatch(arg)
  catch_stack.pop
end

#popenum(arg) ⇒ Object



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

def popenum(arg)
  @enum_stack.pop
end

#popfinally(arg) ⇒ Object



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

def popfinally(arg)
  finally_stack.pop
end

#popscope(arg) ⇒ Object



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

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

#popsp(arg) ⇒ Object



419
420
421
# File 'lib/twostroke/runtime/vm_frame.rb', line 419

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

#push(arg) ⇒ Object

instructions



75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/twostroke/runtime/vm_frame.rb', line 75

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



423
424
425
# File 'lib/twostroke/runtime/vm_frame.rb', line 423

def pushcatch(arg)
  catch_stack.push arg
end

#pushfinally(arg) ⇒ Object



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

def pushfinally(arg)
  finally_stack.push arg
end

#pushsp(arg) ⇒ Object



415
416
417
# File 'lib/twostroke/runtime/vm_frame.rb', line 415

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

#regexp(arg) ⇒ Object



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

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

#ret(arg) ⇒ Object



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

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

#sal(arg) ⇒ Object



268
269
270
271
272
# File 'lib/twostroke/runtime/vm_frame.rb', line 268

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



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

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



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

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

#set(arg) ⇒ Object



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

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

#setindex(arg) ⇒ Object



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

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



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

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

#slr(arg) ⇒ Object



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

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



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

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



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

def this(arg)
  stack.push @this
end

#thiscall(arg) ⇒ Object



97
98
99
100
101
102
103
# File 'lib/twostroke/runtime/vm_frame.rb', line 97

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)
  stack.push fun.call(scope, Types.to_object(stack.pop), args)
end

#true(arg) ⇒ Object



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

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

#typeof(arg) ⇒ Object



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

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



256
257
258
# File 'lib/twostroke/runtime/vm_frame.rb', line 256

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

#with(arg) ⇒ Object



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

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

#xor(arg) ⇒ Object



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

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