h8 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/README.md +68 -4
- data/ext/h8/extconf.rb +29 -10
- data/ext/h8/h8.cpp +16 -0
- data/ext/h8/h8.h +159 -0
- data/ext/h8/js_gate.h +196 -0
- data/ext/h8/main.cpp +162 -0
- data/ext/h8/object_wrap.h +138 -0
- data/ext/h8/ruby_wrap.h +32 -0
- data/hybrid8.gemspec +23 -4
- data/lib/h8.rb +9 -0
- data/lib/h8/context.rb +21 -0
- data/lib/h8/value.rb +77 -0
- data/lib/h8/version.rb +1 -1
- data/spec/context_spec.rb +39 -0
- data/spec/js_gate_spec.rb +162 -0
- data/spec/spec_helper.rb +17 -0
- metadata +39 -8
- data/lib/hybrid8.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7353dcd3c307b93e4f7fd68ccbc7af260e51d09c
|
4
|
+
data.tar.gz: 8603a9cfb342305c5dfcf156bf92d595bde23a34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94056bd7b5378d50ceefd3322479e38b3eb122ee019a0c4ce4a9531bb447fa062e26393c4a5738d97138eaf973a6627b55002126850133fe59c6a9b652028c96
|
7
|
+
data.tar.gz: b1a98f23583bc8cb77ffd35bae2cb1f3d71ea6b9e6f6553149ee2162b468ff9474654d6069d5aa68bb8c0cd97713b26caa24b08604f4b52bbd8b8cca92cc5eb5
|
data/.rspec
ADDED
data/README.md
CHANGED
@@ -1,11 +1,30 @@
|
|
1
|
-
# Hybrid8
|
1
|
+
# Hybrid8, aka H8
|
2
2
|
|
3
|
-
_Warning_ this gem
|
3
|
+
_Warning_ this gem has yet limited functionality and is under main development. Versions suitable
|
4
|
+
for an open beta test will start from 0.1.*.
|
4
5
|
|
5
|
-
Therubyracer gem alternative to work with ruby 2.1+ in multithreaded environment
|
6
|
+
Therubyracer gem alternative to effectively work with ruby 2.1+ in multithreaded environment in an
|
7
|
+
effective and GC-safe way. Should be out of the box replacement for most scenarios.
|
8
|
+
|
9
|
+
Special features:
|
10
|
+
|
11
|
+
- care about wrapped Ruby objects lifespan (are kept until either ruby or javascript context
|
12
|
+
reference wrapped object). In no way GC will not reclaim ruby object that is in use by the
|
13
|
+
javascript context
|
14
|
+
|
15
|
+
- care about wrapped Javascript objects lifetime the same. Referenced JS items will not be recycled
|
16
|
+
while there are ruby objects referencing it. It also means that once created H8::Context will not
|
17
|
+
be reclaimed as long as there is at least one active wrapped object returned from the script.
|
6
18
|
|
7
19
|
## Installation
|
8
20
|
|
21
|
+
### Prerequisites
|
22
|
+
|
23
|
+
You should have installed libv8, use latest version with v8::Isolate and v8::Locker. This version
|
24
|
+
may not find you installation, contact me if you have problems, I'll tune it up.
|
25
|
+
|
26
|
+
### Setting up
|
27
|
+
|
9
28
|
Add this line to your application's Gemfile:
|
10
29
|
|
11
30
|
gem 'hybrid8'
|
@@ -20,10 +39,55 @@ Or install it yourself as:
|
|
20
39
|
|
21
40
|
## Usage
|
22
41
|
|
23
|
-
|
42
|
+
Is generally like therubyracer gem. Create context, set variables, run scripts.
|
43
|
+
|
44
|
+
require 'h8'
|
45
|
+
|
46
|
+
res = H8::Context.eval "({foo: 'hello', bar: 'world'});"
|
47
|
+
puts "#{res.foo} #{res.bar}"
|
48
|
+
|
49
|
+
another way to access attributes:
|
50
|
+
|
51
|
+
puts res['foo']
|
52
|
+
|
53
|
+
The same works with arrays:
|
54
|
+
|
55
|
+
res = H8::Context.eval "['foo', bar'];"
|
56
|
+
puts res[1]
|
57
|
+
|
58
|
+
To set context variables:
|
59
|
+
|
60
|
+
cxt = H8::Context.new some: 'value'
|
61
|
+
cxt[:pi] = 3.1415
|
62
|
+
|
63
|
+
You can return function and call it from ruby:
|
64
|
+
|
65
|
+
fun = cxt.eval "(function pi_n(n) { return pi * n; })"
|
66
|
+
p fun(2)
|
67
|
+
|
68
|
+
The same you can return objects and call its member functions - if a member is a function,
|
69
|
+
it will be called with given arguments:
|
70
|
+
|
71
|
+
res = H8::Context.eval <<-End
|
72
|
+
function cls(base) {
|
73
|
+
this.base = base;
|
74
|
+
this.someVal = 'hello!';
|
75
|
+
this.noArgs = function() { return 'world!'};
|
76
|
+
this.doAdd = function(a, b) {
|
77
|
+
return a + b + base;
|
78
|
+
}
|
79
|
+
}
|
80
|
+
new cls(100);
|
81
|
+
End
|
82
|
+
res.someVal.should == 'hello!'
|
83
|
+
res.noArgs.should == 'world!'
|
84
|
+
res.doAdd(10, 1).should == 111
|
24
85
|
|
25
86
|
## Contributing
|
26
87
|
|
88
|
+
Note that at this early point of development you better first talk to me to not to reinvent the
|
89
|
+
wheel.
|
90
|
+
|
27
91
|
1. Fork it ( https://github.com/[my-github-username]/hybrid8/fork )
|
28
92
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
93
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
data/ext/h8/extconf.rb
CHANGED
@@ -21,21 +21,23 @@ abort 'missing malloc()' unless have_func 'malloc'
|
|
21
21
|
abort 'missing free()' unless have_func 'free'
|
22
22
|
|
23
23
|
# Give it a name
|
24
|
-
extension_name = '
|
24
|
+
extension_name = 'h8'
|
25
25
|
|
26
|
+
dir_config('v8', '/Users/sergeych/dev/v8', '/Users/sergeych/dev/v8/lib')
|
26
27
|
|
27
28
|
dir_config(extension_name)
|
28
29
|
ok = true
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
31
|
+
unless have_header('include/v8.h')
|
32
|
+
$stderr.puts "can't find v8.h, install libv8 3.25.30+ first"
|
33
|
+
ok = false
|
34
|
+
end
|
35
|
+
|
36
|
+
unless have_library('v8_base') && have_library('v8_snapshot') && have_library('v8_libplatform') \
|
37
|
+
&& have_library('v8_libbase') && have_library('icuuc') && have_library('icudata')
|
38
|
+
$stderr.puts "can't find libv8"
|
39
|
+
ok = false
|
40
|
+
end
|
39
41
|
|
40
42
|
|
41
43
|
# This test is actually due to a Clang 3.3 shortcoming, included in OS X 10.9,
|
@@ -60,3 +62,20 @@ else
|
|
60
62
|
raise "Unable to build, correct above errors and rerun"
|
61
63
|
end
|
62
64
|
|
65
|
+
# LIBV8_COMPATIBILITY = '~> 3.30'
|
66
|
+
#
|
67
|
+
# begin
|
68
|
+
# require 'rubygems'
|
69
|
+
# gem 'libv8', LIBV8_COMPATIBILITY
|
70
|
+
# rescue Gem::LoadError
|
71
|
+
# warn "Warning! Unable to load libv8 #{LIBV8_COMPATIBILITY}."
|
72
|
+
# rescue LoadError
|
73
|
+
# warn "Warning! Could not load rubygems. Please make sure you have libv8 #{LIBV8_COMPATIBILITY} installed."
|
74
|
+
# ensure
|
75
|
+
# require 'libv8'
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# Libv8.configure_makefile
|
79
|
+
|
80
|
+
create_makefile('h8/h8')
|
81
|
+
|
data/ext/h8/h8.cpp
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#include "h8.h"
|
2
|
+
|
3
|
+
Local<Value> h8::H8::gateObject(VALUE ruby_value) const {
|
4
|
+
if ( Qtrue == rb_funcall(ruby_value, id_is_a, 1, value_class)) {
|
5
|
+
JsGate *gate;
|
6
|
+
Data_Get_Struct(ruby_value, JsGate, gate);
|
7
|
+
if( gate->h8 != this ) {
|
8
|
+
rb_raise(h8_exception, "H8::Value is bound to other H8::Context");
|
9
|
+
return Undefined(isolate);
|
10
|
+
}
|
11
|
+
else
|
12
|
+
return gate->value();
|
13
|
+
}
|
14
|
+
rb_raise(h8_exception, "Object gate is not implemented");
|
15
|
+
return Undefined(isolate);
|
16
|
+
}
|
data/ext/h8/h8.h
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
#ifndef _h8_h
|
2
|
+
#define _h8_h
|
3
|
+
|
4
|
+
#include <include/v8.h>
|
5
|
+
#include <ruby.h>
|
6
|
+
#include <iostream>
|
7
|
+
|
8
|
+
using namespace v8;
|
9
|
+
using namespace std;
|
10
|
+
|
11
|
+
extern VALUE h8_exception;
|
12
|
+
extern VALUE h8_class;
|
13
|
+
extern VALUE value_class;
|
14
|
+
|
15
|
+
extern ID id_is_a;
|
16
|
+
|
17
|
+
//#include <ruby/thread.h>
|
18
|
+
|
19
|
+
namespace h8 {
|
20
|
+
|
21
|
+
template<class T> inline void t(const T& x) {
|
22
|
+
cout << x << endl << flush;
|
23
|
+
}
|
24
|
+
|
25
|
+
class H8 {
|
26
|
+
public:
|
27
|
+
|
28
|
+
class Scope: public HandleScope {
|
29
|
+
v8::Isolate::Scope isolate_scope;
|
30
|
+
v8::Context::Scope context_scope;
|
31
|
+
H8* rcontext;
|
32
|
+
public:
|
33
|
+
Scope(H8* cxt) :
|
34
|
+
HandleScope(cxt->getIsolate()), isolate_scope(
|
35
|
+
cxt->getIsolate()), context_scope(cxt->getContext()), rcontext(
|
36
|
+
cxt) {
|
37
|
+
}
|
38
|
+
};
|
39
|
+
|
40
|
+
static void init();
|
41
|
+
|
42
|
+
H8() {
|
43
|
+
isolate = Isolate::New();
|
44
|
+
Isolate::Scope isolate_scope(isolate);
|
45
|
+
HandleScope handle_scope(isolate);
|
46
|
+
|
47
|
+
v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(
|
48
|
+
isolate);
|
49
|
+
v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL,
|
50
|
+
global);
|
51
|
+
persistent_context.Reset(isolate, context);
|
52
|
+
}
|
53
|
+
|
54
|
+
Handle<Value> eval(const char* script_utf) {
|
55
|
+
v8::EscapableHandleScope escape(isolate);
|
56
|
+
Local<Value> result;
|
57
|
+
|
58
|
+
Handle<v8::String> script_source = String::NewFromUtf8(isolate,
|
59
|
+
script_utf);
|
60
|
+
v8::Handle<v8::Script> script;
|
61
|
+
v8::TryCatch try_catch;
|
62
|
+
v8::ScriptOrigin origin(String::NewFromUtf8(isolate, "eval"));
|
63
|
+
|
64
|
+
script = v8::Script::Compile(script_source, &origin);
|
65
|
+
|
66
|
+
if (script.IsEmpty()) {
|
67
|
+
report_exception(try_catch);
|
68
|
+
result = Undefined(isolate);
|
69
|
+
} else {
|
70
|
+
result = script->Run();
|
71
|
+
if (try_catch.HasCaught()) {
|
72
|
+
report_exception(try_catch);
|
73
|
+
}
|
74
|
+
}
|
75
|
+
return escape.Escape(result);
|
76
|
+
}
|
77
|
+
|
78
|
+
VALUE eval_to_ruby(const char* script_utf) {
|
79
|
+
// TODO: throw ruby exception on error
|
80
|
+
return to_ruby(eval(script_utf));
|
81
|
+
}
|
82
|
+
|
83
|
+
Handle<Context> getContext() {
|
84
|
+
return Local<Context>::New(isolate, persistent_context);
|
85
|
+
}
|
86
|
+
|
87
|
+
bool isError() const {
|
88
|
+
return is_error;
|
89
|
+
}
|
90
|
+
|
91
|
+
Isolate* getIsolate() const {
|
92
|
+
return isolate;
|
93
|
+
}
|
94
|
+
|
95
|
+
VALUE to_ruby(Handle<Value> value);
|
96
|
+
|
97
|
+
v8::Local<v8::String> js(VALUE val) const {
|
98
|
+
return js(StringValueCStr(val));
|
99
|
+
}
|
100
|
+
|
101
|
+
v8::Local<v8::String> js(const char* str) const {
|
102
|
+
return v8::String::NewFromUtf8(isolate, str);
|
103
|
+
}
|
104
|
+
|
105
|
+
void set_var(VALUE name, VALUE value) {
|
106
|
+
Scope scope(this);
|
107
|
+
getContext()->Global()->Set(js(name), to_js(value));
|
108
|
+
}
|
109
|
+
|
110
|
+
Local<Value> to_js(VALUE ruby_value) const {
|
111
|
+
switch (TYPE(ruby_value)) {
|
112
|
+
case T_STRING:
|
113
|
+
return js(ruby_value);
|
114
|
+
case T_FIXNUM:
|
115
|
+
return v8::Int32::New(isolate, FIX2INT(ruby_value));
|
116
|
+
case T_FLOAT:
|
117
|
+
return v8::Number::New(isolate, NUM2DBL(ruby_value));
|
118
|
+
case T_DATA:
|
119
|
+
case T_OBJECT:
|
120
|
+
return gateObject(ruby_value);
|
121
|
+
default:
|
122
|
+
rb_raise(h8_exception, "can't gate to js: unknown type");
|
123
|
+
}
|
124
|
+
return Undefined(isolate);
|
125
|
+
}
|
126
|
+
|
127
|
+
Local<Value> gateObject(VALUE object) const;
|
128
|
+
|
129
|
+
virtual ~H8() {
|
130
|
+
persistent_context.Reset();
|
131
|
+
}
|
132
|
+
|
133
|
+
private:
|
134
|
+
friend VALUE context_alloc(VALUE klass);
|
135
|
+
friend void rvalue_mark(void* ptr);
|
136
|
+
|
137
|
+
Isolate *isolate;
|
138
|
+
VALUE self;
|
139
|
+
|
140
|
+
void report_exception(v8::TryCatch& tc) {
|
141
|
+
rb_raise(h8_exception, "Failed to compile/execute script");
|
142
|
+
}
|
143
|
+
|
144
|
+
Persistent<Context> persistent_context;
|
145
|
+
|
146
|
+
bool is_error = false;
|
147
|
+
};
|
148
|
+
// Context
|
149
|
+
}
|
150
|
+
|
151
|
+
typedef VALUE (*ruby_method)(...);
|
152
|
+
|
153
|
+
#include "js_gate.h"
|
154
|
+
|
155
|
+
inline VALUE h8::H8::to_ruby(Handle<Value> value) {
|
156
|
+
return JsGate::to_ruby(this, value);
|
157
|
+
}
|
158
|
+
|
159
|
+
#endif
|
data/ext/h8/js_gate.h
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
#ifndef __js_gate_h
|
2
|
+
#define __js_gate_h
|
3
|
+
|
4
|
+
#include "h8.h"
|
5
|
+
|
6
|
+
using namespace v8;
|
7
|
+
|
8
|
+
namespace h8 {
|
9
|
+
|
10
|
+
/**
|
11
|
+
* Interface to anything that could be converted to a Javascipt object. Provides common helpers.
|
12
|
+
*/
|
13
|
+
class JsValue {
|
14
|
+
public:
|
15
|
+
virtual Local<Value> value() const = 0;
|
16
|
+
|
17
|
+
Local<Object> object() const {
|
18
|
+
return value()->ToObject();
|
19
|
+
}
|
20
|
+
|
21
|
+
virtual Isolate* isolate() = 0;
|
22
|
+
};
|
23
|
+
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Gates JS object to ruby environment. Holds persistent reference to the source js object until
|
27
|
+
* ruby object is recycled (then frees it). Note that this ruby object is not meant to be kept alive
|
28
|
+
* by the H8 instance, instead, its owner should.
|
29
|
+
*
|
30
|
+
* Methods of this class do not need the H8::Scope, they create one internally.
|
31
|
+
*/
|
32
|
+
class JsGate : public JsValue {
|
33
|
+
public:
|
34
|
+
/**
|
35
|
+
* Used in the ruby allocator. Do not call unless you know what you do.
|
36
|
+
*/
|
37
|
+
JsGate() {
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Return Ruby object that gates specified Handled javascript object. Ruby object
|
42
|
+
* locks permanently value until get recycled.
|
43
|
+
*/
|
44
|
+
template <class T>
|
45
|
+
static VALUE to_ruby(H8* h8, const Handle<T>& value) {
|
46
|
+
JsGate *gate;
|
47
|
+
VALUE ruby_gate = rb_class_new_instance(0, NULL, value_class);
|
48
|
+
Data_Get_Struct(ruby_gate, JsGate, gate);
|
49
|
+
gate->set(h8, value);
|
50
|
+
return ruby_gate;
|
51
|
+
}
|
52
|
+
|
53
|
+
/**
|
54
|
+
* Reset gate to the specified handle.
|
55
|
+
*/
|
56
|
+
template<class T>
|
57
|
+
void set(H8 *h8, const Handle<T>& val) {
|
58
|
+
this->h8 = h8;
|
59
|
+
persistent_value.Reset(h8->getIsolate(), val);
|
60
|
+
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
* Get ruby string representation
|
64
|
+
*/
|
65
|
+
VALUE to_s() {
|
66
|
+
H8::Scope scope(h8);
|
67
|
+
String::Utf8Value res(value());
|
68
|
+
return *res ? rb_str_new2(*res) : Qnil;
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Get ruby integer representation (FIXNUM)
|
73
|
+
*/
|
74
|
+
VALUE to_i() {
|
75
|
+
H8::Scope scope(h8);
|
76
|
+
return INT2FIX(value()->IntegerValue());
|
77
|
+
}
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Get ruby Float representation (FIXNUM)
|
81
|
+
*/
|
82
|
+
VALUE to_f() {
|
83
|
+
H8::Scope scope(h8);
|
84
|
+
return DBL2NUM(value()->NumberValue());
|
85
|
+
}
|
86
|
+
|
87
|
+
/**
|
88
|
+
* @return true if the object is a primitive integer
|
89
|
+
*/
|
90
|
+
VALUE is_int() {
|
91
|
+
H8::Scope scope(h8);
|
92
|
+
return value()->IsInt32() ? Qtrue : Qfalse;
|
93
|
+
}
|
94
|
+
|
95
|
+
/**
|
96
|
+
* @return true if the object is a primitive float
|
97
|
+
*/
|
98
|
+
VALUE is_float() {
|
99
|
+
H8::Scope scope(h8);
|
100
|
+
return value()->IsNumber() ? Qtrue : Qfalse;
|
101
|
+
}
|
102
|
+
|
103
|
+
/**
|
104
|
+
* @return true if the object is an array
|
105
|
+
*/
|
106
|
+
VALUE is_array() {
|
107
|
+
H8::Scope scope(h8);
|
108
|
+
return value()->IsArray() ? Qtrue : Qfalse;
|
109
|
+
}
|
110
|
+
|
111
|
+
/**
|
112
|
+
* @return true if the object is an object
|
113
|
+
*/
|
114
|
+
VALUE is_object() {
|
115
|
+
H8::Scope scope(h8);
|
116
|
+
return value()->IsObject() ? Qtrue : Qfalse;
|
117
|
+
}
|
118
|
+
|
119
|
+
/**
|
120
|
+
* Retreive JS object attribute and convert it to the ruby wrapper
|
121
|
+
* of the new JsGate instace.
|
122
|
+
*/
|
123
|
+
VALUE get_attribute(VALUE name) {
|
124
|
+
H8::Scope scope(h8);
|
125
|
+
Local<Value> v8_name = v8::String::NewFromUtf8(isolate(), StringValueCStr(name));
|
126
|
+
return h8->to_ruby(object()->Get(v8_name));
|
127
|
+
}
|
128
|
+
|
129
|
+
VALUE get_index(VALUE index) {
|
130
|
+
H8::Scope scope(h8);
|
131
|
+
return h8->to_ruby(object()->Get(NUM2INT(index)));
|
132
|
+
}
|
133
|
+
|
134
|
+
/**
|
135
|
+
* @return true if the object is a primitive string
|
136
|
+
*/
|
137
|
+
VALUE is_string() {
|
138
|
+
H8::Scope scope(h8);
|
139
|
+
return value()->IsString() ? Qtrue : Qfalse;
|
140
|
+
}
|
141
|
+
|
142
|
+
VALUE is_function() {
|
143
|
+
H8::Scope scope(h8);
|
144
|
+
return value()->IsFunction();
|
145
|
+
}
|
146
|
+
|
147
|
+
VALUE is_undefined() {
|
148
|
+
H8::Scope scope(h8);
|
149
|
+
return value()->IsUndefined() ? Qtrue : Qfalse;
|
150
|
+
}
|
151
|
+
|
152
|
+
VALUE call(VALUE args) const {
|
153
|
+
v8::HandleScope scope(h8->getIsolate());
|
154
|
+
return apply(h8->getContext()->Global(), args);
|
155
|
+
}
|
156
|
+
|
157
|
+
VALUE apply(VALUE self, VALUE args) const {
|
158
|
+
v8::HandleScope scope(h8->getIsolate());
|
159
|
+
return apply(h8->gateObject(self), args);
|
160
|
+
}
|
161
|
+
|
162
|
+
VALUE apply(Local<Value> self, VALUE args) const {
|
163
|
+
H8::Scope scope(h8);
|
164
|
+
long count = RARRAY_LEN(args);
|
165
|
+
Local<Value> *js_args = new Local<Value>[count];
|
166
|
+
for (int i = 0; i < count; i++) {
|
167
|
+
js_args[i] = h8->to_js(rb_ary_entry(args, i));
|
168
|
+
}
|
169
|
+
Local<Value> result = object()->CallAsFunction(self, count, js_args);
|
170
|
+
delete[] js_args;
|
171
|
+
return h8->to_ruby(result);
|
172
|
+
}
|
173
|
+
|
174
|
+
virtual Local<Value> value() const {
|
175
|
+
return Local<Value>::New(h8->getIsolate(), persistent_value);
|
176
|
+
}
|
177
|
+
|
178
|
+
virtual Isolate* isolate() {
|
179
|
+
return h8->getIsolate();
|
180
|
+
}
|
181
|
+
|
182
|
+
~JsGate() {
|
183
|
+
persistent_value.Reset();
|
184
|
+
}
|
185
|
+
|
186
|
+
private:
|
187
|
+
friend void rvalue_mark(void* ptr);
|
188
|
+
friend class H8;
|
189
|
+
|
190
|
+
H8 *h8=0;
|
191
|
+
Persistent<Value> persistent_value;
|
192
|
+
};
|
193
|
+
|
194
|
+
}
|
195
|
+
|
196
|
+
#endif
|