twostroke 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. data/lib/twostroke/ast/array.rb +8 -0
  2. data/lib/twostroke/ast/assignment.rb +7 -0
  3. data/lib/twostroke/ast/binary_operators.rb +7 -0
  4. data/lib/twostroke/ast/body.rb +6 -0
  5. data/lib/twostroke/ast/break.rb +4 -0
  6. data/lib/twostroke/ast/call.rb +7 -0
  7. data/lib/twostroke/ast/case.rb +9 -1
  8. data/lib/twostroke/ast/continue.rb +11 -0
  9. data/lib/twostroke/ast/declaration.rb +4 -0
  10. data/lib/twostroke/ast/delete.rb +6 -0
  11. data/lib/twostroke/ast/do_while.rb +7 -0
  12. data/lib/twostroke/ast/false.rb +11 -0
  13. data/lib/twostroke/ast/for_in.rb +8 -0
  14. data/lib/twostroke/ast/for_loop.rb +10 -1
  15. data/lib/twostroke/ast/function.rb +6 -0
  16. data/lib/twostroke/ast/if.rb +8 -0
  17. data/lib/twostroke/ast/index.rb +7 -0
  18. data/lib/twostroke/ast/member_access.rb +6 -0
  19. data/lib/twostroke/ast/multi_expression.rb +7 -0
  20. data/lib/twostroke/ast/new.rb +7 -0
  21. data/lib/twostroke/ast/null.rb +4 -0
  22. data/lib/twostroke/ast/number.rb +4 -0
  23. data/lib/twostroke/ast/object_literal.rb +6 -0
  24. data/lib/twostroke/ast/regexp.rb +4 -0
  25. data/lib/twostroke/ast/return.rb +6 -0
  26. data/lib/twostroke/ast/string.rb +4 -0
  27. data/lib/twostroke/ast/switch.rb +7 -0
  28. data/lib/twostroke/ast/ternary.rb +8 -0
  29. data/lib/twostroke/ast/this.rb +4 -0
  30. data/lib/twostroke/ast/throw.rb +6 -0
  31. data/lib/twostroke/ast/true.rb +11 -0
  32. data/lib/twostroke/ast/try.rb +8 -0
  33. data/lib/twostroke/ast/unary_operators.rb +7 -1
  34. data/lib/twostroke/ast/variable.rb +4 -0
  35. data/lib/twostroke/ast/while.rb +8 -1
  36. data/lib/twostroke/ast/with.rb +16 -0
  37. data/lib/twostroke/compiler/javascript.rb +396 -0
  38. data/lib/twostroke/compiler/tsasm.rb +595 -0
  39. data/lib/twostroke/compiler.rb +8 -0
  40. data/lib/twostroke/parser.rb +47 -9
  41. data/lib/twostroke/runtime/lib/array.js +144 -0
  42. data/lib/twostroke/runtime/lib/array.rb +71 -0
  43. data/lib/twostroke/runtime/lib/boolean.rb +23 -0
  44. data/lib/twostroke/runtime/lib/console.rb +13 -0
  45. data/lib/twostroke/runtime/lib/date.rb +65 -0
  46. data/lib/twostroke/runtime/lib/error.rb +36 -0
  47. data/lib/twostroke/runtime/lib/function.js +0 -0
  48. data/lib/twostroke/runtime/lib/function.rb +45 -0
  49. data/lib/twostroke/runtime/lib/math.rb +36 -0
  50. data/lib/twostroke/runtime/lib/number.rb +65 -0
  51. data/lib/twostroke/runtime/lib/object.js +0 -0
  52. data/lib/twostroke/runtime/lib/object.rb +55 -0
  53. data/lib/twostroke/runtime/lib/regexp.rb +28 -0
  54. data/lib/twostroke/runtime/lib/string.rb +86 -0
  55. data/lib/twostroke/runtime/lib/undefined.rb +7 -0
  56. data/lib/twostroke/runtime/lib.rb +42 -0
  57. data/lib/twostroke/runtime/scope.rb +120 -0
  58. data/lib/twostroke/runtime/types/array.rb +79 -0
  59. data/lib/twostroke/runtime/types/boolean.rb +23 -0
  60. data/lib/twostroke/runtime/types/boolean_object.rb +25 -0
  61. data/lib/twostroke/runtime/types/function.rb +83 -0
  62. data/lib/twostroke/runtime/types/null.rb +11 -3
  63. data/lib/twostroke/runtime/types/number.rb +31 -0
  64. data/lib/twostroke/runtime/types/number_object.rb +25 -0
  65. data/lib/twostroke/runtime/types/object.rb +169 -20
  66. data/lib/twostroke/runtime/types/regexp.rb +31 -0
  67. data/lib/twostroke/runtime/types/string.rb +16 -0
  68. data/lib/twostroke/runtime/types/string_object.rb +52 -0
  69. data/lib/twostroke/runtime/types/undefined.rb +11 -3
  70. data/lib/twostroke/runtime/types/value.rb +14 -0
  71. data/lib/twostroke/runtime/types.rb +133 -4
  72. data/lib/twostroke/runtime/vm.rb +25 -0
  73. data/lib/twostroke/runtime/vm_frame.rb +459 -0
  74. data/lib/twostroke/runtime.rb +6 -5
  75. data/lib/twostroke/tokens.rb +20 -8
  76. data/lib/twostroke.rb +3 -1
  77. metadata +41 -7
  78. data/lib/twostroke/runtime/context.rb +0 -33
  79. data/lib/twostroke/runtime/environment.rb +0 -13
  80. data/lib/twostroke/runtime/types/basic_type.rb +0 -5
@@ -0,0 +1,55 @@
1
+ module Twostroke::Runtime
2
+ Lib.register do |scope|
3
+ proto = Types::Object.new
4
+
5
+ obj = Types::Function.new(->(scope, this, args) {
6
+ if args.length.zero? || args[0].is_a?(Types::Null) || args[0].is_a?(Types::Undefined)
7
+ Types::Object.new
8
+ else
9
+ Types.to_object(args[0])
10
+ end
11
+ }, nil, "Object", [])
12
+ #obj.prototype is Function, lets set its prototype to proto
13
+ obj.prototype.prototype = proto
14
+ obj.proto_put "prototype", proto
15
+ scope.set_var "Object", obj
16
+
17
+ proto.proto_put "toString", Types::Function.new(->(scope, this, args) {
18
+ if this.is_a? Types::Primitive
19
+ Types.to_string(this).string
20
+ else
21
+ Types::String.new "[object #{this._class ? this._class.name : "Object"}]"
22
+ end
23
+ }, nil, "toString", [])
24
+ proto.proto_put "valueOf", Types::Function.new(->(scope, this, args) { this }, nil, "valueOf", [])
25
+ proto.proto_put "hasOwnProperty", Types::Function.new(->(scope, this, args) {
26
+ Types::Boolean.new Types.to_object(this || Types::Undefined.new).has_own_property(Types.to_string(args[0] || Types::Undefined.new).string)
27
+ }, nil, "hasOwnProperty", [])
28
+ proto.proto_put "isPrototypeOf", Types::Function.new(->(scope, this, args) {
29
+ if args[0].is_a? Types::Object
30
+ proto = args[0].prototype
31
+ this = Types.to_object(this || Types::Undefined.new)
32
+ while proto.is_a?(Types::Object)
33
+ return Types::Boolean.new(true) if this == proto
34
+ proto = proto.prototype
35
+ end
36
+ end
37
+ Types::Boolean.new false
38
+ }, nil, "isPrototypeOf", [])
39
+ proto.proto_put "propertyIsEnumerable", Types::Function.new(->(scope, this, args) {
40
+ this = Types.to_object(this || Types::Undefined.new)
41
+ prop = Types.to_string(args[0] || Types::Undefined.new).string
42
+ if this.has_accessor(prop)
43
+ Types::Boolean.new this.accessors[prop][:enumerable]
44
+ elsif this.has_property
45
+ Types::Boolean.new true
46
+ else
47
+ Types::Boolean.new false
48
+ end
49
+ }, nil, "propertyIsEnumerable", [])
50
+
51
+ Types::Object.set_global_prototype proto
52
+ Types::Object.define_singleton_method(:constructor_function) { obj }
53
+ scope.global_scope.root_object.prototype = proto
54
+ end
55
+ end
@@ -0,0 +1,28 @@
1
+ module Twostroke::Runtime
2
+ Lib.register do |scope|
3
+ regexp = Types::RegExp.constructor_function
4
+ scope.set_var "RegExp", regexp
5
+ proto = Types::Object.new
6
+ proto.proto_put "toString", Types::Function.new(->(scope, this, args) {
7
+ Lib.throw_type_error "RegExp.prototype.toString is not generic" unless this.is_a?(Types::RegExp)
8
+ this.primitive_value
9
+ }, nil, "toString", [])
10
+ proto.define_own_property "global", get: ->(this) {
11
+ Types::Boolean.new this.global
12
+ }, writable: false, enumerable: false, configurable: false
13
+ proto.define_own_property "ignoreCase", get: ->(this) {
14
+ Types::Boolean.new((this.regexp.options & Regexp::IGNORECASE) != 0)
15
+ }, writable: false, enumerable: false, configurable: false
16
+ proto.define_own_property "multiline", get: ->(this) {
17
+ Types::Boolean.new((this.regexp.options & Regexp::MULTILINE) != 0)
18
+ }, writable: false, enumerable: false, configurable: false
19
+ proto.define_own_property "source", get: ->(this) {
20
+ Types::String.new this.regexp.source
21
+ }, writable: false, enumerable: false, configurable: false
22
+ proto.proto_put "test", Types::Function.new(->(scope, this, args) {
23
+ Lib.throw_type_error "RegExp.prototype.test is not generic" unless this.is_a?(Types::RegExp)
24
+ Types::Boolean.new((Types.to_string(args[0] || Undefined.new).string =~ this.regexp) != nil)
25
+ }, nil, "test", [])
26
+ regexp.proto_put "prototype", proto
27
+ end
28
+ end
@@ -0,0 +1,86 @@
1
+ module Twostroke::Runtime
2
+ Lib.register do |scope|
3
+ obj = Types::StringObject.constructor_function
4
+ scope.set_var "String", obj
5
+
6
+ proto = Types::Object.new
7
+ # String.prototype.toString
8
+ proto.proto_put "toString", Types::Function.new(->(scope, this, args) {
9
+ if this.is_a?(Types::StringObject)
10
+ Types::String.new(this.string)
11
+ else
12
+ Lib.throw_type_error "String.prototype.toString is not generic"
13
+ end
14
+ }, nil, "toString", [])
15
+ # String.prototype.valueOf
16
+ proto.proto_put "valueOf", Types::Function.new(->(scope, this, args) { this.is_a?(Types::StringObject) ? Types::String.new(this.string) : Types.to_primitive(this) }, nil, "valueOf", [])
17
+ # String.prototype.split
18
+ proto.proto_put "split", Types::Function.new(->(scope, this, args) {
19
+ sep = Types.to_string(args[0] || Types::Undefined.new).string
20
+ str = Types.to_string(this).string
21
+ Types::Array.new (if args[1]
22
+ str.split sep, Types.to_uint32(args[1])
23
+ else
24
+ str.split sep
25
+ end).map { |s| Types::String.new s }
26
+ }, nil, "split", [])
27
+ # String.prototype.length
28
+ proto.define_own_property "length", get: ->(this) { Types::Number.new this.string.size }, writable: false, enumerable: false
29
+ # String.prototype.replace
30
+ proto.proto_put "replace", Types::Function.new(->(scope, this, args) {
31
+ sobj = Types.to_string(this)
32
+ s = sobj.string
33
+
34
+ find = args[0] || Types::Undefined.new
35
+ re = find.is_a?(Types::RegExp) ? find.regexp : Regexp.new(Regexp.escape Types.to_string(find).string)
36
+ global = find.is_a?(Types::RegExp) && find.global
37
+
38
+ replace = args[1] || Types::Undefined.new
39
+ callback = replace.respond_to?(:call) ? replace : ->(*_) { replace }
40
+
41
+ retn = ""
42
+ offset = 0
43
+ loop do
44
+ md = re.match s, offset
45
+ break unless md && (offset.zero? || global)
46
+ retn << md.pre_match[offset..-1]
47
+ retn << Types.to_string(callback.(scope, nil, [*md.to_a.map { |c| Types::String.new c }, Types::Number.new(md.begin 0), sobj])).string
48
+ offset = md.end 0
49
+ end
50
+
51
+ retn << s[offset..-1]
52
+
53
+ Types::String.new retn
54
+ }, nil, "replace", [])
55
+ =begin
56
+ find = args[0] || Types::Undefined.new
57
+ find = Types.to_string(find).string unless find.is_a?(Types::RegExp)
58
+ replace = args[1] || Types::Undefined.new
59
+ replace = Types.to_string(replace).string unless replace.respond_to? :call
60
+ Types::String.new(if find.is_a?(String)
61
+ if replace.is_a?(String)
62
+ s.sub find, replace
63
+ else
64
+ s.sub(find) { |m| Types.to_string(replace.call(scope, nil, [Types::String.new(m), Types::Number.new(s.index m), sobj])).string }
65
+ end
66
+ else
67
+ m = s.method(find.global ? :gsub : :sub)
68
+ if replace.is_a?(String)
69
+ m.(find.regexp, replace)
70
+ else
71
+ offset = 0
72
+ m.(find.regexp) do |m|
73
+ idx = s.index m, offset
74
+ offset = idx + m.size
75
+ Types.to_string(replace.call(scope, nil, [Types::String.new(m), Types::Number.new(idx), sobj])).string
76
+ end
77
+ end
78
+ end)
79
+ =end
80
+ obj.proto_put "prototype", proto
81
+
82
+ obj.proto_put "fromCharCode", Types::Function.new(->(scope, this, args) {
83
+ Types::String.new args.map { |a| Types.to_number(a).number.to_i.chr }.join
84
+ }, nil, "fromCharCode", [])
85
+ end
86
+ end
@@ -0,0 +1,7 @@
1
+ module Twostroke::Runtime
2
+ Lib.register do |scope|
3
+ scope.set_var "undefined", Types::Undefined.new
4
+
5
+ scope.set_var "crap", Types::Function.new(->(scope,this,args) { require "pry"; pry binding }, nil, "crap", [])
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ module Twostroke::Runtime
2
+ module Lib
3
+ INITIALIZERS = []
4
+
5
+ def self.setup_environment(vm)
6
+ INITIALIZERS.each { |i| i.arity == 1 ? i.(vm.global_scope) : i.(vm.global_scope, vm) }
7
+ end
8
+
9
+ def self.register(&bk)
10
+ INITIALIZERS << bk
11
+ end
12
+
13
+ require File.expand_path("../lib/object.rb", __FILE__)
14
+ Dir.glob File.expand_path("../lib/*.rb", __FILE__) do |f|
15
+ require f
16
+ end
17
+
18
+ class << self
19
+ Dir.glob File.expand_path("../lib/*.js", __FILE__) do |f|
20
+ INITIALIZERS << ->(global_scope, vm) {
21
+ privileged_scope = global_scope.close
22
+ privileged_scope.declare :twostroke_lib_set
23
+ privileged_scope.set_var :twostroke_lib_set, Types::Function.new(->(scope, this, args) {
24
+ vm.lib[Types.to_string(args[0] || Types::Undefined.new).string.intern] = args[1] || Types::Undefined.new
25
+ }, nil, "twostroke_lib_set", [])
26
+
27
+ parser = Twostroke::Parser.new(Twostroke::Lexer.new(File.read f))
28
+ parser.parse
29
+
30
+ compiler = Twostroke::Compiler::TSASM.new parser.statements, "lib_#{f}_"
31
+ compiler.compile
32
+
33
+ compiler.bytecode.each do |k,v|
34
+ vm.bytecode[k] = v
35
+ end
36
+
37
+ vm.execute :"lib_#{f}_main", privileged_scope
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,120 @@
1
+ module Twostroke::Runtime
2
+ class Scope
3
+ attr_reader :parent
4
+
5
+ def initialize(parent = nil)
6
+ @locals = {}
7
+ @parent = parent
8
+ end
9
+
10
+ def get_var(var)
11
+ if @locals.has_key? var
12
+ @locals[var]
13
+ else
14
+ @parent.get_var(var)
15
+ end
16
+ end
17
+
18
+ def set_var(var, value)
19
+ if @locals.has_key? var
20
+ @locals[var] = value
21
+ else
22
+ @parent.set_var(var, value)
23
+ end
24
+ end
25
+
26
+ def has_var(var)
27
+ @locals.has_key?(var) || parent.has_var(var)
28
+ end
29
+
30
+ def declare(var)
31
+ @locals[var] = Types::Undefined.new
32
+ end
33
+
34
+ def close
35
+ Scope.new self
36
+ end
37
+
38
+ def global_scope
39
+ @global_scope ||= parent.global_scope
40
+ end
41
+ end
42
+
43
+ class ObjectScope
44
+ attr_reader :object, :parent
45
+
46
+ def initialize(object, parent)
47
+ @parent, @object = parent, object
48
+ end
49
+
50
+ def get_var(var)
51
+ if object.has_property var.to_s
52
+ object.get var.to_s
53
+ else
54
+ parent.get_var var
55
+ end
56
+ end
57
+
58
+ def set_var(var, value)
59
+ if object.has_property var.to_s
60
+ object.put var.to_s, value
61
+ else
62
+ parent.set_var var.to_s, value
63
+ end
64
+ end
65
+
66
+ def has_var
67
+ object.has_property(var.to_s) || parent.has_var(var)
68
+ end
69
+
70
+ def declare(var)
71
+ parent.declare var
72
+ end
73
+
74
+ def close
75
+ Scope.new self
76
+ end
77
+
78
+ def global_scope
79
+ @global_scope ||= parent.global_scope
80
+ end
81
+ end
82
+
83
+ class GlobalScope
84
+ attr_reader :root_object, :root_name, :vm
85
+
86
+ def initialize(vm, root_name = "window", root_object = nil)
87
+ @root_name = root_name
88
+ @root_object = root_object || Types::Object.new
89
+ @root_object.put root_name.to_s, @root_object
90
+ @vm = vm
91
+ end
92
+
93
+ def get_var(var)
94
+ if @root_object.has_property var.to_s
95
+ @root_object.get var.to_s
96
+ else
97
+ Lib.throw_reference_error "undefined variable #{var}"
98
+ end
99
+ end
100
+
101
+ def has_var(var)
102
+ @root_object.has_property var.to_s
103
+ end
104
+
105
+ def declare(var)
106
+ end
107
+
108
+ def close
109
+ Scope.new self
110
+ end
111
+
112
+ def set_var(var, value)
113
+ @root_object.put var.to_s, value
114
+ end
115
+
116
+ def global_scope
117
+ self
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,79 @@
1
+ module Twostroke::Runtime
2
+ module Types
3
+ class Array < Object
4
+ def self.constructor_function
5
+ @@constructor_function ||=
6
+ Function.new(->(scope, this, args) do
7
+ if args.length.zero?
8
+ Array.new
9
+ elsif args.length == 1
10
+ Array.new([nil] * Twostroke::Runtime::Types.to_uint32(args[0]))
11
+ else
12
+ Array.new args
13
+ end
14
+ end, nil, "Array", [])
15
+ end
16
+
17
+ attr_accessor :items
18
+ def initialize(items = [])
19
+ @prototype = Array.constructor_function.get("prototype")
20
+ super()
21
+ @items = items
22
+ end
23
+
24
+ def length
25
+ items.size
26
+ end
27
+
28
+ def get(prop, this = self)
29
+ if prop =~ /\A\d+\z/
30
+ items[prop.to_i] || Undefined.new
31
+ else
32
+ super prop, this
33
+ end
34
+ end
35
+
36
+ def put(prop, val, this = self)
37
+ if prop =~ /\A\d+\z/
38
+ items[prop.to_i] = val
39
+ else
40
+ super prop, val, this
41
+ end
42
+ end
43
+
44
+ def has_property(prop)
45
+ if prop =~ /\A\d+\z/
46
+ i = prop.to_i
47
+ i >= 0 && i < items.size && !items[i].nil?
48
+ else
49
+ super prop
50
+ end
51
+ end
52
+
53
+ def has_own_property(prop)
54
+ if prop =~ /\A\d+\z/
55
+ i = prop.to_i
56
+ i >= 0 && i < items.size && !items[i].nil?
57
+ else
58
+ super prop
59
+ end
60
+ end
61
+
62
+ def delete(prop)
63
+ if prop =~ /\A\d+\z/
64
+ i = prop.to_i
65
+ if i >= 0 && i < items.size && !items[i].nil?
66
+ items[i] = nil
67
+ return true
68
+ end
69
+ end
70
+ super prop
71
+ end
72
+
73
+ def each_enumerable_property(&bk)
74
+ (0...items.size).map(&:to_s).each &bk
75
+ super &bk
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,23 @@
1
+ module Twostroke::Runtime::Types
2
+ class Boolean < Primitive
3
+ def self.true
4
+ @@true ||= Boolean.new(true)
5
+ end
6
+ def self.false
7
+ @@false ||= Null.new(false)
8
+ end
9
+
10
+ attr_reader :boolean
11
+ def initialize(boolean)
12
+ @boolean = boolean
13
+ end
14
+
15
+ def ===(other)
16
+ other.is_a?(Boolean) && boolean == other.boolean
17
+ end
18
+
19
+ def typeof
20
+ "boolean"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ module Twostroke::Runtime::Types
2
+ class BooleanObject < Object
3
+ def self.constructor_function
4
+ @@constructor_function ||=
5
+ Function.new(->(scope, this, args) {
6
+ if this.constructing?
7
+ Twostroke::Runtime::Types.to_object(Twostroke::Runtime::Types.to_boolean(args[0] || Undefined.new))
8
+ else
9
+ Twostroke::Runtime::Types.to_boolean(args[0])
10
+ end
11
+ }, nil, "Boolean", [])
12
+ end
13
+
14
+ attr_reader :boolean
15
+ def initialize(boolean)
16
+ @prototype = BooleanObject.constructor_function.get("prototype")
17
+ super()
18
+ @boolean = boolean
19
+ end
20
+
21
+ def primitive_value
22
+ Boolean.new boolean
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,83 @@
1
+ module Twostroke::Runtime::Types
2
+ class Function < Object
3
+ def self.constructor_function
4
+ unless defined?(@@constructor_function)
5
+ @@constructor_function = nil # lock the Function constructor out from here...
6
+ @@created_funcs = 0
7
+ @@constructor_function = Function.new(->(scope, this, args) {
8
+ formal_parameters = (args.size > 1 ? args[0...-1] : []).map { |a| Twostroke::Runtime::Types.to_string(a).string }
9
+ src = Twostroke::Runtime::Types.to_string(args[-1] || Undefined.new).string
10
+
11
+ parser = Twostroke::Parser.new(Twostroke::Lexer.new(src))
12
+ parser.parse
13
+
14
+ @@created_funcs += 1
15
+ fun_ast = Twostroke::AST::Function.new name: "anonymous", arguments: formal_parameters, statements: parser.statements
16
+ compiler = Twostroke::Compiler::TSASM.new [fun_ast], "runtime_created_#{@@created_funcs}_"
17
+ compiler.compile
18
+
19
+ vm = scope.global_scope.vm
20
+ compiler.bytecode.each do |k,v|
21
+ vm.bytecode[k] = v
22
+ end
23
+
24
+ global_scope = vm.global_scope
25
+ fun = Function.new(->(_scope, _this, _args) {
26
+ Twostroke::Runtime::VM::Frame.new(vm, :"runtime_created_#{@@created_funcs}_fn_1", fun).execute(global_scope, _this, _args)
27
+ }, src, "anonymous", formal_parameters)
28
+ }, nil, "Function", [])
29
+ @@constructor_function.proto_put "constructor", @@constructor_function
30
+ @@constructor_function._class = @@constructor_function
31
+ end
32
+ @@constructor_function
33
+ end
34
+
35
+ attr_reader :arguments, :name, :source, :function
36
+ def initialize(function, source, name, arguments)
37
+ @function = function
38
+ @source = source
39
+ @name = name
40
+ @arguments = arguments
41
+ # setting @_class to Function's constructor would result in a stack overflow,
42
+ # so we'll set it to nil and patch things up after @@constructor_function has
43
+ # been set
44
+ @_class = nil unless defined?(@@constructor_function)
45
+ super()
46
+ @prototype = nil
47
+ proto_put "prototype", Object.new
48
+ end
49
+
50
+ def prototype
51
+ @prototype ||= Function.constructor_function.get("prototype")
52
+ end
53
+
54
+ def has_instance(obj)
55
+ return false unless obj.is_a? Object
56
+ o = get "prototype"
57
+ Twostroke::Runtime::Lib.throw_type_error "Function prototype not an object" unless o.is_a? Object
58
+ loop do
59
+ obj = obj.prototype
60
+ return false unless obj.is_a?(Object)
61
+ return true if obj == o
62
+ end
63
+ end
64
+
65
+ def typeof
66
+ "function"
67
+ end
68
+
69
+ def primitive_value
70
+ String.new "function #{name}(#{arguments.join ","}) { #{source || "[native code]"} }"
71
+ end
72
+
73
+ def call(upper_scope, this, args)
74
+ retn_val = function.(upper_scope, this || upper_scope.global_scope.root_object, args)
75
+ # prevent non-Value objects being returned to javascript
76
+ if retn_val.is_a? Value
77
+ retn_val
78
+ else
79
+ Undefined.new
80
+ end
81
+ end
82
+ end
83
+ end
@@ -1,7 +1,15 @@
1
1
  module Twostroke::Runtime::Types
2
- class Null < BasicType
3
- def self.null
4
- @@null ||= Null.new
2
+ class Null < Primitive
3
+ def self.new
4
+ @@null ||= Null.allocate
5
+ end
6
+
7
+ def ===(other)
8
+ other.is_a?(Null)
9
+ end
10
+
11
+ def typeof
12
+ "object"
5
13
  end
6
14
  end
7
15
  end
@@ -0,0 +1,31 @@
1
+ module Twostroke::Runtime::Types
2
+ class Number < Primitive
3
+ attr_reader :number
4
+ def initialize(number)
5
+ @number = number
6
+ end
7
+
8
+ def ===(other)
9
+ if number.zero? && other.is_a?(Number) && other.number.zero?
10
+ # in javascript, -0 and 0 are not equal
11
+ # in ruby they are, and the only way to check if a number is -0 is with #to_s
12
+ # please correct me if there's a better way
13
+ number.to_s[0] == other.number.to_s[0]
14
+ else
15
+ other.is_a?(Number) && number == other.number
16
+ end
17
+ end
18
+ def typeof
19
+ "number"
20
+ end
21
+ def zero?
22
+ number.zero?
23
+ end
24
+ def nan?
25
+ number.is_a?(Float) && number.nan?
26
+ end
27
+ def infinite?
28
+ number.is_a?(Float) && number.infinite?
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ module Twostroke::Runtime::Types
2
+ class NumberObject < Object
3
+ def self.constructor_function
4
+ @@constructor_function ||=
5
+ Function.new(->(scope, this, args) {
6
+ if this.constructing?
7
+ Twostroke::Runtime::Types.to_object(Twostroke::Runtime::Types.to_number(args[0] || Undefined.new))
8
+ else
9
+ Twostroke::Runtime::Types.to_number(args[0])
10
+ end
11
+ }, nil, "Number", [])
12
+ end
13
+
14
+ attr_reader :number
15
+ def initialize(number)
16
+ @number = number
17
+ @prototype = NumberObject.constructor_function.get("prototype")
18
+ super()
19
+ end
20
+
21
+ def primitive_value
22
+ Number.new number
23
+ end
24
+ end
25
+ end