Bacardi Project: Node.js 네이티브모듈을위한 오픈소스바인딩제네레이터 방진호 (zino@chromium.org) 2017.10.26
Bacardi Project 란?
Bacardi Project 란..
Node.js 에서 Native Module 을바인딩하는방법에대한오픈소스이다.
그런데이미기존 Node.js 에는바인딩을위한방법들이제공되고있다.
그러나그방법이아름답지않았다.
Bacardi Project 는그러한문제를분석하고
아름답게해결하는방안을모색한다.
다음과같은순서로진행됩니다. JS에서왜 Native Module을사용하는가? 예제를통해알아보는기존의 Native Module 연동방식기존의문제점파헤치기 Chromium에서 Blink와 V8은어떻게연결되나? Node.js에서 Native Module과 V8은어떻게연결되나? WebIDL Auto Binding 구현하기 (Bacardi의구현원리 ) Bacardi Project 활용하기 01 02 03 04 05 06 07
JS 에서왜 Native Module 을 사용하는가?
성능 (Performance)
Low Level APIs
기존에잘만들어진코드가 Native 일때
어쨌든 Node.js 를사용한다면사용중인패키지의 30% 는 Native 이다.
예제를통해알아보는 기존의 Native Module 연동방식
sum() 함수를구현해보자 (JS 호출 ) JS let s = sum([1, 2, 3, 4, 5, 6, 7, 8, 9]); JS function sum(elements) { let s = 0; elements.foreach(element => { s += element; }); return s; }
sum() 함수를구현해보자 (Native 호출 ) JS let s = sum([1, 2, 3, 4, 5, 6, 7, 8, 9]); Native int sum(std::vector<int> elements) { int s = 0; for (int i = 0; i < elements.size(); i++) s += elements[i]; return s; }?
그런데 Javascript 에서어떻게호출할까?
기존의 Native Module 연동방식 아마우리가원했던게이런것은아니지말입니다.. var spawn = require('child_process').spawn; var sum = spawn('sum', [...]); sum.stdout.on('data', result => { process(result);... });
기존의 Native Module 연동방식 Node.js C++ Addon https://nodejs.org/dist/latest-v8.x/docs/api/addons.html NAN(Native Abstraction for Node) Node.js C++ Addon 이 V8 Public API 를사용하기때문에 V8 변화에민감하다. Node.js 를업데이트할때마다문제가발생하는것을해결. https://github.com/nodejs/nan N-API Latest! Node.js 8.0 이후부터지원, 8.6 에서 Stable Release NAN 과유사한방식이지만, ABI-Stable 을보장. ( 즉, 재컴파일불필요 ) V8 Public API 가아닌 Node.js 에서한번 Wrapping 하여제공하므로다른엔진으로교체도가능. https://nodejs.org/dist/latest-v8.x/docs/api/n-api.html
N-API 에이르기까지많은개선이있었지만, Binding 코드를작성하는것을위한개선은없었다.
N-API 를사용하여 Binding 해보자 다음의예제는 N-API 를사용하여 Native Binding 을하고있다. napi_value Sum(napi_env, napi_callback_info info) { napi_status status; size_t args = 1; napi_value args[1]; napi_status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); if (argc < 1) { napi_throw_type_error(env, nullptr, " "); return nullptr; } uint32_t length = 0; napi_get_array_length(env, args[0], &length); double sum = 0; for (int i = 0; i < length; i++) { napi_value element; napi_get_element(env, i, &element); napi_valuetype valuetype; napi_typeof(env, element, &valuetype); if (napi_valuetype!= napi_number) { napi_throw_type_error(env, nullptr, " "); return nullptr; } 그러나.. 6 줄짜리 sum() 함수가 35 줄이된다. double value; napi_get_value_double(env, element, &value); sum += value; } napi_value js_sum; napi_create_double(env, sum, &js_sum); return js_sum; }
N-API 를사용하여 Binding 해보자 N-API 를사용하여 Binding 을하면 Javascript 에서호출할수있다. const sum = require( sum ); let s = sum([1, 2, 3, 4, 5, 6, 7, 8, 9]);
기존의문제점파헤치기
지금부터코드가많이나오는데.. 굳이그코드들을이해하실필요는없습니다!
문제점 1: Argument Check napi_value Sum(napi_env, napi_callback_info info) { napi_status status; size_t args = 1; napi_value args[1]; napi_status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); // 문제점1. JS argument는 Runtime에결정되므로개발자가직접체크해야한다. if (argc < 1) { napi_throw_type_error(env, nullptr, " "); return nullptr; }
문제점 2: Type Checking for (int i = 0; i < length; i++) { napi_value element; napi_get_element(env, i, &element); // 문제점2. 각 Array Element의 Type Checking도직접해야한다. napi_valuetype valuetype; napi_typeof(env, element, &valuetype); if (napi_valuetype!= napi_number) { napi_throw_type_error(env, nullptr, " "); return nullptr; }
문제점 3: Type Converting } // 문제점3. JS에서 Native Type Converting을직접해야한다. double value; napi_get_value_double(env, element, &value); sum += value; // 문제점3. Native에서 JS Type Converting을직접해야한다. napi_value js_sum; napi_create_double(env, sum, &js_sum); return js_sum;
문제점 4: Memory Management uint32_t length = 10000; for (int i = 0; i < length; i++) { napi_value element; napi_get_element(env, i, &element); Push 내가아는 Stack napi_value length }... Pop...
문제점 4: Memory Management uint32_t length = 10000; HandleScopeStack Heap for (int i = 0; i < length; i++) { // napi_get_element() 를호출할때마다 // 새로운 object를생성하고 HandleScope에 // 쌓이게됨. napi_value element; napi_get_element(env, i, &element);... } i = 9999 i = 9998 i = 4 i = 3 i = 2 i = 1 i = 0 napi_value napi_value napi_value napi_value napi_value napi_value napi_value
문제점 4: Memory Management for (int i = 0; i < length; i++) { napi_handle_scope scope; napi_status status = napi_open_handle_scope(env, &scope); napi_value element; napi_get_element(env, i, &element);... } napi_close_handle_scope(env, scope);
문제점 5: Readability // 문제점5. Binding 코드가많이삽입되면서가독성이떨어진다. int sum(std::vector<int> elements) { int s = 0; for (int i = 0; i < elements.size(); i++) s += elements[i]; return s; } napi_value Sum(napi_env, napi_callback_info info) { napi_status status; size_t args = 1; napi_value args[1]; napi_status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); if (argc < 1) { napi_throw_type_error(env, nullptr, " "); return nullptr; } uint32_t length = 0; napi_get_array_length(env, args[0], &length); double sum = 0; for (int i = 0; i < length; i++) { napi_value element; napi_get_element(env, i, &element); napi_valuetype valuetype; napi_typeof(env, element, &valuetype); if (napi_valuetype!= napi_number) { napi_throw_type_error(env, nullptr, " "); return nullptr; } double value; napi_get_value_double(env, element, &value); sum += value; } napi_value js_sum; napi_create_double(env, sum, &js_sum); return js_sum; }
요약 기존의방법은 TypeChecking/Converting 과같은노가다를반복해야한다. Memory 관리 Mechanism 이다르고 Binding 을위해사용되는 API 를다룰줄알아야하므로 V8, N-API, JS, C++ 과같은지식모두를필요로한다. 전체적인코드의복잡도가증가한다.
Chromium 에서 Blink 와 V8 은 어떻게연결되나?
왜 Chromium 을살펴보나?
Chromium 은 V8 의또다른 Embedder 이다.
V8 (Javascript Engine) Blink (Rendering Engine) V8 (Javascript Engine) Node.js Native Module
document.getelementbyid();
<div id="hello"></div> <script> let div = document.getelementbyid( hello ); </script>
V8 Engine 과 Blink Engine 의관계 <div id="hello"></div> <script> let div = document.getelementbyid( hello ); </script> Chromium V8 Loader Engine
V8 Engine 과 Blink Engine 의관계 <div id="hello"></div> <script> let div = document.getelementbyid( hello ); </script> Chromium V8 Loader Engine DOM Parsing V8 Engine Blink Engine
V8 Engine 과 Blink Engine 의관계 <div id="hello"></div> <script> let div = document.getelementbyid( hello ); </script> Chromium V8 Loader Engine V8 Engine JS Evaluate <script> document.getelementbyid( hello ); </script> DOM Parsing V8 Engine Blink Engine
V8 Engine 과 Blink Engine 의관계 <div id="hello"></div> <script> let div = document.getelementbyid( hello ); </script> Chromium V8 Loader Engine V8 Engine JS Evaluate <script> document.getelementbyid( hello ); </script> DOM Parsing V8 Engine Blink Engine Actually, called document.getelementbyid();
V8 Engine 과 Blink Engine 의관계 <div id="hello"></div> <script> let div = document.getelementbyid( hello ); </script> Chromium V8 Loader Engine V8 Engine JS Evaluate <script> document.getelementbyid( hello ); </script> DOM Parsing V8 Engine Blink Engine Return JS Wrapper Object let div = document.getelementbyid( hello ); Actually, called document.getelementbyid();
V8 Engine 과 Blink Engine 의관계 <div id="hello"></div> <script> let div = document.getelementbyid( hello ); </script> Chromium V8 Loader Engine V8 Engine JS Evaluate <script> document.getelementbyid( hello ); </script> V8 Binding DOM Parsing V8 Engine Blink Engine Return JS Wrapper Object let div = document.getelementbyid( hello ); Type Checking/Converting Manage Isolate and Context Actually, called document.getelementbyid();
V8 Engine 과 Blink Engine 의관계 <div id="hello"></div> <script> let div = document.getelementbyid( hello ); </script> Chromium V8 Loader Engine V8 Engine JS Evaluate <script> document.getelementbyid( hello ); </script> WebIDL Binding DOM Parsing V8 Engine Blink Engine Return JS Wrapper Object let div = document.getelementbyid( hello ); Auto-generated!! Actually, called document.getelementbyid();
V8 Binding 이란? Chromium에서 V8과 Blink를연결하는코드이다. 사용자가직접짜지않고 WebIDL으로부터자동생성 (auto-generation) 된다. WebIDL은 JS Engine과 Web Engine을연결하는 Web 표준에의해정의된다. https://heycam.github.io/webidl/ https://www.w3.org/tr/api-design/
코드레벨로살펴보는 WebIDL // WebIDL [Constructor] interface Calculator { double sum(sequence<long> elements); };
코드레벨로살펴보는 WebIDL // WebIDL [Constructor] interface Calculator { double sum(sequence<long> elements); }; VS napi_value Sum(napi_env, napi_callback_info info) { napi_status status; size_t args = 1; napi_value args[1]; napi_status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); if (argc < 1) { napi_throw_type_error(env, nullptr, " "); return nullptr; } uint32_t length = 0; napi_get_array_length(env, args[0], &length); double sum = 0; for (int i = 0; i < length; i++) { napi_value element; napi_get_element(env, i, &element); napi_valuetype valuetype; napi_typeof(env, element, &valuetype); if (napi_valuetype!= napi_number) { napi_throw_type_error(env, nullptr, " "); return nullptr; } double value; napi_get_value_double(env, element, &value); sum += value; } napi_value js_sum; napi_create_double(env, sum, &js_sum); return js_sum; }
코드레벨로살펴보는 WebIDL // User s Native Code class Calculator { public: double Sum(std::vector<int> elements) { int sum = 0; for (int i = 0; i < elements.size(); i++) sum += elements[i]; return sum; } };
코드레벨로살펴보는 WebIDL // User s JS code var calculator = new Calculator(); console.log(calculator.sum([1, 2, 3, 4, 5, 6, 7, 8, 9]));
요약 Chromium 에서 V8 과 Blink 의관계는 Node.js 에서 V8 과 Native Module 의관계와동등하다. V8 과 Blink 를연결하는 Layer 를 V8 Binding(WebIDL Binding) 이라고부른다. V8 Binding(WebIDL Binding) 은 WebIDL 을이용해 Auto-generated 된다.
Node.js Native Module 과 V8 은어떻게연결되나?
const fs = require('fs'); let contents = fs.readfilesync('temp.txt', 'utf8');
V8 Engine 과 Native Module 의관계 Node Runtime const fs = require('fs'); let contents = fs.readfilesync('temp.txt', 'utf8'); V8 Engine
V8 Engine 과 Native Module 의관계 Node Runtime const fs = require('fs'); let contents = fs.readfilesync('temp.txt', 'utf8'); V8 Engine JS Evaluate V8 Engine
V8 Engine 과 Native Module 의관계 Node Runtime const fs = require('fs'); let contents = fs.readfilesync('temp.txt', 'utf8'); V8 Engine JS Evaluate V8 Engine V8 Engine Native Module Actually, called open() and read()
V8 Engine 과 Native Module 의관계 Node Runtime const fs = require('fs'); let contents = fs.readfilesync('temp.txt', 'utf8'); V8 Engine JS Evaluate V8 Engine V8 Engine Native Module Return JS Wrapper Object Actually, called open() and read()
V8 Engine 과 Native Module 의관계 Node Runtime const fs = require('fs'); let contents = fs.readfilesync('temp.txt', 'utf8'); V8 Engine JS Evaluate V8 Engine V8 Binding V8 Engine Native Module Return JS Wrapper Object Type Checking/Converting Manage Isolate and Context Actually, called open() and read()
복습 : N-API 를사용하여 Binding 해보자 이미우리가여러번말했듯이이러한과정을반복한다. napi_value Sum(napi_env, napi_callback_info info) { napi_status status; size_t args = 1; napi_value args[1]; napi_status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); if (argc < 1) { napi_throw_type_error(env, nullptr, " "); return nullptr; } uint32_t length = 0; napi_get_array_length(env, args[0], &length); double sum = 0; for (int i = 0; i < length; i++) { napi_value element; napi_get_element(env, i, &element); napi_valuetype valuetype; napi_typeof(env, element, &valuetype); if (napi_valuetype!= napi_number) { napi_throw_type_error(env, nullptr, " "); return nullptr; } 이런식으로장인정신을발휘하면 잘만들수있다! double value; napi_get_value_double(env, element, &value); sum += value; } napi_value js_sum; napi_create_double(env, sum, &js_sum); return js_sum; }
복습 : V8 Engine 과 Blink Engine 의관계 <div id="hello"></div> <script> let div = document.getelementbyid( hello ); </script> Chromium V8 Loader Engine V8 Engine JS Evaluate <script> document.getelementbyid( hello ); </script> WebIDL Binding DOM Parsing V8 Engine Blink Engine Return JS Wrapper Object let div = document.getelementbyid( hello ); Auto-generated!! Actually, called document.getelementbyid();
Node.js 에 Auto Binding 을적용한다면.. IDL을제외하고는추가적인 Binding 코드를직접작성하지않아도된다. 바인딩코드와실제 User Implementation이완벽히분리된다. V8( 또는 N-API) 을이해하지않아도된다. 안전하고빠르게바인딩할수있다.
실제사례 : Open CV Binding // Open CV Matrix를생성하는 5개의함수오버로딩.. let m1 = new Matrix(); let m2 = new Matrix(rows, cols); let m3 = new Matrix(10, 20, CV_32F); let m4 = new Matrix(10, 20, CV_32F, [1, 2, 3]); let m5 = new Matrix(m4, 10, 20, 30, 40);
실제사례 : Open CV Binding // 이 Matrix 의실제 Binding 구현은다음과유사하다. // https://github.com/peterbraden/node-opencv/blob/master/src/matrix.cc#l132 Matrix* mat; if (info.length() == 0) { mat = new Matrix; } else if (info.length() == 2 && info[0]->isint32() && info[1]->isint32()) { mat = new Matrix(info[0]->IntegerValue(), info[1]->integervalue()); } else if (info.length() == 3 && info[0]->isint32() && info[1]->isint32() && info[2]->isint32()) { mat = new Matrix(info[0]->IntegerValue(), info[1]->integervalue(), info[2]->integervalue()); } else if (info.length() == 4 && info[0]->isint32() && info[1]->isint32() && info[2]->isint32() && info[3]->isarray()) { mat = new Matrix(info[0]->IntegerValue(), info[1]->integervalue(), info[2]->integervalue(), info[3]->toobject()); else { // if (info.length() == 5) {... }
실제사례 : Open CV Binding // 만약 WebIDL Binding을사용했다면.. [ Constructor(), Constructor(long rows, long cols), Constructor(long rows, long cols, long type), Constructor(long rows, long cols, long type, sequence<long> scalar), Constructor(Matrix m, long x, long y, long z, long w) ] interface Matrix {... };
실제사례 : Open CV Binding // 만약당신이 WebIDL 고수였다면.. [ Constructor(), Constructor(long rows, long cols, optional long type, optional sequence<long> scalar), Constructor(Matrix m, long x, long y, long z, long w) ] interface Matrix {... };
요약 Node.js 에서 V8 과 Native Module 사이의 Binding 은개발자가직접한땀한땀개발해야만했다. Chromium 에서사용되는 WebIDL Binding 을도입한다면, 그러한장인정신을발휘하지않고도쉽고빠르게개발할수있다.
Node.js를위한 WebIDL Auto Binding 구현하기 (Bacardi의구현원리 )
What should we do? WebIDL 을해석한후 N-API 를사용하는적절한 Binding 코드를생성한다.
Blink 에서 WebIDL 이처리되는과정 WebIDL User Native Implementation
Blink 에서 WebIDL 이처리되는과정 WebIDL Input IDL Parser User Native Implementation
Blink 에서 WebIDL 이처리되는과정 WebIDL Input IDL Parser Binding Code Generator User Native Implementation
Blink 에서 WebIDL 이처리되는과정 WebIDL Input IDL Parser Binding Code Generator Input Code Template User Native Implementation
Blink 에서 WebIDL 이처리되는과정 WebIDL Input IDL Parser Binding Code Generator Input Code Template Build User Native Implementation Generated Code Based on V8
Blink 에서 WebIDL 이처리되는과정 WebIDL Input IDL Parser Binding Code Generator Input Code Template Build User Native Implementation Reference Generated Code Based on V8
Blink 에서 WebIDL 이처리되는과정 WebIDL Input IDL Parser Binding Code Generator Input Code Template Build User Native Implementation Reference Generated Code Based on V8 Build Build Output Binary
What should we do? IDL Parser 를구현한다. (Front End) Code Generator 를구현한다. (Back End)
How to implement IDL Parser 를구현한다. (Front End) 이미잘구현된 WebIDL Parser 가있다 : https://github.com/w3c/webidl2.js 우리는 AST(Abstract Syntax Tree) 를 IDL Definitions 로만들어주는것만하면된다. Code Generator 를구현한다. (Back End)
How to implement IDL Parser 를구현한다. (Front End) 이미잘구현된 WebIDL Parser 가있다 : https://github.com/w3c/webidl2.js 우리는 AST(Abstract Syntax Tree) 를 IDL Definitions 로만들어주는것만하면된다. Code Generator 를구현한다. (Back End) Python 의 Jinja2 를 JS 로포팅한 Nunjucks 가있다 : https://github.com/mozilla/nunjucks 우리는 C++ Binding Code (N-API 를사용한 ) 를잘생성해주는로직을짜기만하면된다.
IDL Parser (Front End) Compiler의 FrontEnd의역할. IR(Intermediate Representation) 을생성. Constructing 단계에서다음의두가지요소가추가적으로필요함. Extended attribute validation Dependency resolution. IDL Tokens AST (Abstract Syntax Tree) IR (IDL Definitions) Lexing Parsing Constructing
IDL Parser (Front End) [Constructor] interface Hello { double test(world world); }; [{ type: 'interface', name: 'Hello', partial: false, members: [ [...] ], inheritance: null, extattrs: [ [...] ] }] Parsing Dependency Resolution [{... name: 'Hello', members: [ [{ idltype: 'World,... }] ], }] Constructing [Constructor] interface World { readonly attribute value; };
Code Generator (Back End) Compiler 의 BackEnd 의역할. IR 을 Input 으로최종결과물 (binding code) 생성. 코드생성을위해 Nunjucks Template Engine 을사용. Code Template IR (IDL Definitions) Contexts Native Binding Code V8 Engine Logic Processing Template Processing
Code Generator (Back End) {% for argument in member.arguments %} auto {{argument.name}} = NativeTypeTraits<IDL{{argument.type camelcase-}} >::NativeValue(info.Env(), info[{{loop.index - 1}}]); {% endfor %} {% if member.type!= "void" %}auto return_value = {% endif %} {% if member.is_static -%} {{name}}::{% else %} impl_->{% endif %} {{member.name camelcase}}({{-member.arguments[0].name-}} {%- for i in range(1, member.arguments.length) -%}, {{member.arguments[i].name-}} {% endfor %}); return JSTypeTraits(info.Env(), return_value);
Code Generator (Back End) {% for argument in member.arguments %} auto {{argument.name}} = NativeTypeTraits<IDL{{argument.type camelcase-}} >::NativeValue(info.Env(), info[{{loop.index - 1}}]); {% endfor %} {% if member.type!= "void" %}auto return_value = {% endif %} {% if member.is_static -%} {{name}}::{% else %} impl_->{% endif %} {{member.name camelcase}}({{-member.arguments[0].name-}} {%- for i in range(1, member.arguments.length) -%}, {{member.arguments[i].name-}} {% endfor %}); return JSTypeTraits(info.Env(), return_value);
Code Generator (Back End) if (info.length()!= 2) { Napi::RangeError::New(info.Env(), "Invalid").ThrowAsJavaScriptException(); return Napi::Value(); } // 복잡한 Type Mapping 및 Converting 은개발자에게노출하지않는다. double number1 = NativeTypeTraits<IDLDouble>::NativeValue(info.Env(), info[0]); double number2 = NativeTypeTraits<IDLDouble>::NativeValue(info.Env(), info[1]); // 사용자가작성한 User Native Implementation 을여기서호출한다. auto return_value = impl_->add(number1, number2); return JSTypeTraits(info.Env(), return_value);
Code Generator (Back End) class Calculator { public: Calculator() {} } // 개발자는 JS Engine의 public API가어떻게동작하는지알필요가없다. // WebIDL Spec에따라 IDL type이정확한 platform object type과 mapping된다. double Add(double number1, double number2) { return number1 + number2; }
요약 Node.js 에 WebIDL Binding 을구현하기위해서는 IDL Parser 와 Code Generator 두가지를구현해야한다.
Bacardi Project 활용하기
Introduce Bacardi Project Node.js 에서 Chromium 이사용하는 WebIDL Auto Binding 을적용하기위한오픈소스프로젝트입니다. https://github.com/lunchclass/bacardi Chromium Committer 들이주축으로만들어갑니다.
How to test Bacardi # Git clone repository $ git clone https://github.com/lunchclass/bacardi
How to test Bacardi # Git clone repository $ git clone https://github.com/lunchclass/bacardi # If you are using MacOS $ xcode-select install # If you are using Linux $ sudo apt-get install g++ git make python wget
How to test Bacardi # Git clone repository $ git clone https://github.com/lunchclass/bacardi # If you are using MacOS $ xcode-select install # If you are using Linux $ sudo apt-get install g++ git make python wget # Build & Test $./bacardi build &&./bacardi test
Details 자세한것은 Repository 를참조. https://github.com/lunchclass/bacardi 테스트가아닌간단한예제는 examples 디렉토리를참조. https://github.com/lunchclass/bacardi/tree/master/examples
Bacardi with Electron # Build for Electron & Run Electron app $./bacardi build_electron &&./bacardi electron
Bacardi with Electron - SimRank 두객체의유사도를구하는알고리즘.
Bacardi with Electron - SimRank 만약여러분이사용하고싶은구현이 Node.js 에존재하지않는다면..
Bacardi with Electron - SimRank 구글링 (Googling) 을해보니 SimRank 의 C++ 구현은존재한다. https://github.com/roukaour/simrank
Bacardi with Electron - SimRank
Bacardi with Electron - SimRank 이것을그대로 Javascript 로 binding 하여 Electron app 에출력해보자. https://github.com/lunchclass/bacardi/pull/135 [ Constructor(), Constructor(long k, double c) ] interface ElectronNative { void addedge(string head, string tail); void calculatesimrank(); double similarity(string node1, string node2); };
Bacardi with Electron - SimRank
Bacardi with Electron - SimRank
Bacardi 의향후계획 2017년 4분기까지 C++ Binding 구현완료 - NPM 배포 OpenCV & Tensor Flow Binding Bacardi로옮기기 Cross Platform Build 지원 Welcome to contribution (https://github.com/lunchclass/bacardi)
요약 Node.js 에 WebIDL Binding 을구현하기위해서는 IDL Parser 와 Code Generator 두가지를구현해야한다. 모두가같은노력을할필요없으므로오픈소스프로젝트 (Bacardi Project) 로진행중이며, 이를 Node.js Native Module 개발에활용할수있다.
Q & A zino@chromium.org
THANK YOU Copyright c 2017 SAMSUNG ELECTRONICS. ALL RIGHTS RESERVED