Code Monkey home page Code Monkey logo

ducc's People

Contributors

adumbidiot avatar alanhoff avatar kylewlacy avatar skylerlipthay avatar wyozi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

ducc's Issues

Support multiple Duktape contexts with a shared heap

Currently, Ducc::new() always creates a new Duktape heap for the context. However, this means values cannot be shared across contexts except through serialization.

The Duktape guide has a section on contexts and heaps that describes how to create a context using a shared heap with duk_push_thread or duk_push_thread_new_globalenv.

For prior art, the v8-rs crate exposes a similar concept with the Context and Isolate types. So, library users would first call Isolate::new() to create a new isolated v8 instance (analogous to a Duktape heap), then Context::new(&isolate) to create the context (analogous to a Duktape context).

Warnings When Compiling on Stable

I noticed a lot of warnings while trying to compile this library on stable rustc 1.39.0 (4560ea788 2019-11-04) on Windows 10:

warning: trait objects without an explicit `dyn` are deprecated
   --> ducc\src\ducc.rs:106:74
    |
106 |     pub fn set_user_data<K, T>(&mut self, key: K, data: T) -> Option<Box<Any + 'static>>
    |                                                                          ^^^^^^^^^^^^^ help: use `dyn`: `dyn Any + 'static`
    |
    = note: `#[warn(bare_trait_objects)]` on by default

warning: trait objects without an explicit `dyn` are deprecated
   --> ducc\src\ducc.rs:132:65
    |
132 |     pub fn remove_user_data(&mut self, key: &str) -> Option<Box<Any + 'static>> {
    |                                                                 ^^^^^^^^^^^^^ help: use `dyn`: `dyn Any + 'static`

warning: trait objects without an explicit `dyn` are deprecated
   --> ducc\src\ducc.rs:439:31
    |
439 |     pub cancel_fn: Option<Box<Fn() -> bool>>,
    |                               ^^^^^^^^^^^^ help: use `dyn`: `dyn Fn() -> bool`

warning: trait objects without an explicit `dyn` are deprecated
  --> ducc\src\error.rs:48:23
   |
48 |     ExternalError(Box<RuntimeError + 'static>),
   |                       ^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn RuntimeError + 'static`

warning: trait objects without an explicit `dyn` are deprecated
   --> ducc\src\error.rs:307:6
    |
307 | impl RuntimeError {
    |      ^^^^^^^^^^^^ help: use `dyn`: `dyn RuntimeError`

warning: trait objects without an explicit `dyn` are deprecated
   --> ducc\src\error.rs:313:45
    |
313 |             unsafe { Some(&*(self as *const RuntimeError as *const T)) }
    |                                             ^^^^^^^^^^^^ help: use `dyn`: `dyn RuntimeError`

warning: trait objects without an explicit `dyn` are deprecated
  --> ducc\src\types.rs:33:9
   |
33 |     Box<Fn(&'ducc Ducc, Value<'ducc>, Values<'ducc>) -> Result<Value<'ducc>> + 'a>;
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn Fn(&'ducc Ducc, Value<'ducc>, Values<'ducc>) -> Result<Value<'ducc>> + 'a`

warning: trait objects without an explicit `dyn` are deprecated
  --> ducc\src\types.rs:35:47
   |
35 | pub(crate) type AnyMap = BTreeMap<String, Box<Any + 'static>>;
   |                                               ^^^^^^^^^^^^^ help: use `dyn`: `dyn Any + 'static`

warning: use of deprecated item 'std::sync::ONCE_INIT': the `new` function is now preferred
 --> ducc\src\util.rs:8:23
  |
8 | use std::sync::{Once, ONCE_INIT};
  |                       ^^^^^^^^^
  |
  = note: `#[warn(deprecated)]` on by default

warning: use of deprecated item 'std::sync::ONCE_INIT': the `new` function is now preferred
   --> ducc\src\util.rs:317:25
    |
317 |     static INIT: Once = ONCE_INIT;
    |                         ^^^^^^^^^ help: replace the use of the deprecated item: `Once::new()`

warning: unused boxed boxed `std::ops::Fn` trait object that must be used
   --> ducc\src\function.rs:118:9
    |
118 |         Box::from_raw(ffi::duk_get_pointer(ctx, -1) as *mut Callback);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `#[warn(unused_must_use)]` on by default
    = note: closures are lazy and do nothing unless called

Store Ducc values inside userdata

Hey, great library! I have a usecase where I'm trying to store hooks (ducc::Functions) inside userdata, so they can be called from Rust code later when needed. However, the 'static lifetime requirement for userdata values is making this quite difficult.

How viable do you think it would be to change the set_user_data data lifetime to for instance 'ducc? As far as I understand, this would then make it possible to store Ducc values inside userdata.

Implement accessors (duk_def_prop)

Hello again!

Some code I'm writing would benefit from duk_def_prop, i.e. implementing properties with getters and setters straight in Rust. It's doable by calling defineProperty in JS, but doing it in Rust might feel a bit nicer.

I would be open to implementing the feature, but I'm not sure what the API should look like since duk_def_prop accepts quite a varying set of arguments.

Unhandled fatal error causes stack overflow

When testing JS errors I found that there is not a clear way to handle this error without the program crashing.

Rust code:

use ducc::{Ducc, ExecSettings};

fn main() {
    let ducc = Ducc::new();
    ducc.exec("throw 'test'", None, ExecSettings::default()).unwrap()
}

Running this code on rust 1.42.0 stable-i686-pc-windows-msvc results in:

     Running `target\debug\ducctest.exe`
fatal error from duktape: uncaught: 'cannot write property [Symbol hidden \x27?error\x27] of \x27test\x27'
error: process didn't exit successfully: `target\debug\ducctest.exe` (exit code: 0xc0000409, STATUS_STACK_BUFFER_OVERRUN)

Is there another way to wrap exec so that these errors can be handled? Thank you.

Use `DUK_USE_DATE_NOW_WINDOWS` flag for Windows 7 support

The library currently only works on Windows 7 if it was compiled on a Windows 7 (or older) machine. See this code from duk_config_default.h:

/* GetSystemTimePreciseAsFileTime() available from Windows 8:
 * https://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx
 */
#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) || defined(DUK_USE_DATE_NOW_WINDOWS)
/* User forced provider. */
#else
#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602)
#define DUK_USE_DATE_NOW_WINDOWS_SUBMS
#else
#define DUK_USE_DATE_NOW_WINDOWS
#endif
#endif

We use this library and ran into this issue, see latex-lsp/texlab#101. Therefore, it would be nice to have a feature flag for ducc to enable Windows 7 support.

Undefined symbols for architecture x86_64: "DUK_VARARGS"

Hello there, I'm trying the following code:

use ducc::{Ducc};

fn main() {
    let ducc = Ducc::new();

    let func = ducc.create_function(|inv| {
        let (a, b): (usize, usize) = inv.args.into(inv.ducc).uwrap();
        Ok(a + b)
    });

    ducc.globals().set("add", func).unwrap();
}

But the following error occurs:

error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" "-m64" "-L" "/Users/alanhoff/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib" "/Users/alanhoff/Projects/ductest/target/debug/deps/$uctest-4ed857ff77ff2a19.15uc7ccwcv2sggoy.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.1h2mhh1lezhmpl1b.rcgu.o" "/Users/alanhoff/Projects/duc$est/target/debug/deps/ductest-4ed857ff77ff2a19.1o9sak0djwp0cu9n.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.1tl0jw7in6toutg7.rcgu.o" "/User$/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.204he97yf1e9g301.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.2d212p7h$3vspiqx.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.2kvknh1drs918t6p.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4e$857ff77ff2a19.2tbbw9d7852eyw12.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.35ntzgf66nbpfb7o.rcgu.o" "/Users/alanhoff/Projects/ductest/targe$/debug/deps/ductest-4ed857ff77ff2a19.36x511b9a354ayv.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.3ewclrwmtpeldov8.rcgu.o" "/Users/alanhoff/$rojects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.3fut51dpensxzkbn.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.3xts6afcmj37ti5j.rc$u.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.4f7hnqpd7k57m48q.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2$19.4ha6rjmfou7z0ced.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.4muhw3oestqi42wo.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/dep$/ductest-4ed857ff77ff2a19.59fpeycpfwwwbhlm.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.5c9u4ogk35o2haqn.rcgu.o" "/Users/alanhoff/Projects/d$ctest/target/debug/deps/ductest-4ed857ff77ff2a19.dplgs6d9c1051ds.rcgu.o" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.x4ygs4r5khhuakb.rcgu.o" "-o" "$Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19" "/Users/alanhoff/Projects/ductest/target/debug/deps/ductest-4ed857ff77ff2a19.15n3gzqcp366zbjw.rcgu.o" "-$l,-dead_strip" "-nodefaultlibs" "-L" "/Users/alanhoff/Projects/ductest/target/debug/deps" "-L" "/Users/alanhoff/Projects/ductest/target/debug/build/ducc-sys-56fed41fa3b28b30/out" "$L" "/Users/alanhoff/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib" "/Users/alanhoff/Projects/ductest/target/debug/deps/libducc-41fdc7277bd506d2$rlib" "/Users/alanhoff/Projects/ductest/target/debug/deps/libducc_sys-6a7f687dcd36b241.rlib" "/Users/alanhoff/Projects/ductest/target/debug/deps/libcesu8-6b61ba793b4c8e70.rlib" "/U$ers/alanhoff/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/libstd-d8e5437473efc55d.rlib" "/Users/alanhoff/.rustup/toolchains/nightly-x86_64-app$e-darwin/lib/rustlib/x86_64-apple-darwin/lib/libpanic_unwind-978f8f77602b2ff8.rlib" "/Users/alanhoff/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/$ib/libbacktrace_sys-a67ac3e54cec1633.rlib" "/Users/alanhoff/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/librustc_demangle-95f0e5e52206fff4.rl$b" "/Users/alanhoff/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/libhashbrown-9d60b8ff6d472505.rlib" "/Users/alanhoff/.rustup/toolchains/night$y-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/librustc_std_workspace_alloc-b50df887d56819fa.rlib" "/Users/alanhoff/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/ru$
tlib/x86_64-apple-darwin/lib/libunwind-b1a59c9cbbe22ae5.rlib" "/Users/alanhoff/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/liblibc-534a39ff35$
41e78.rlib" "/Users/alanhoff/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/liballoc-c8c18cd805f8cabb.rlib" "/Users/alanhoff/.rustup/toolchains/$
ightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/librustc_std_workspace_core-624ba95ad15c67c4.rlib" "/Users/alanhoff/.rustup/toolchains/nightly-x86_64-apple-darwin/li$
/rustlib/x86_64-apple-darwin/lib/libcore-4d41bd37e688d569.rlib" "/Users/alanhoff/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/libcompiler_buil$
ins-df33d2651a37ff5a.rlib" "-lSystem" "-lresolv" "-lc" "-lm"
  = note: Undefined symbols for architecture x86_64:
            "DUK_VARARGS", referenced from:
                ducc::function::create_callback::he06e3bfbb8a50d3c in libducc-41fdc7277bd506d2.rlib(ducc-41fdc7277bd506d2.ducc.u323l1gq-cgu.15.rcgu.o)
               (maybe you meant: _DUK_VARARGS)
          ld: symbol(s) not found for architecture x86_64
          clang: error: linker command failed with exit code 1 (use -v to see invocation)

This is my Cargo.toml

[package]
name = "ductest"
version = "0.1.0"
authors = ["Alan Hoffmeister"]
edition = "2018"

[dependencies]
ducc = { git = "https://github.com/SkylerLipthay/ducc.git" }

And my machine's info:

cargo 1.36.0-nightly (c4fcfb725 2019-05-15)
rustc 1.36.0-nightly (6afcb5628 2019-05-19)
Darwin Alans-MacBook-Pro.local 16.7.0 Darwin Kernel Version 16.7.0: Wed Feb 27 00:29:57 PST 2019; root:xnu-3789.73.43~1/RELEASE_X86_64 x86_64

Add support for arm

while compiling for arm, got the following errors:

build errors
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/util.rs:117:38
    |
117 |     ffi::duk_get_prop_string(ctx, 0, ERROR_KEY.as_ptr());
    |                                      ^^^^^^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/util.rs:121:38
    |
121 |     ffi::duk_put_prop_string(ctx, 0, ERROR_KEY.as_ptr());
    |                                      ^^^^^^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/util.rs:157:43
    |
157 |         ffi::duk_put_prop_string(ctx, -2, ERROR_KEY.as_ptr());
    |                                           ^^^^^^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/util.rs:167:43
    |
167 |         ffi::duk_get_prop_string(ctx, -1, ERROR_KEY.as_ptr());
    |                                           ^^^^^^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/util.rs:171:43
    |
171 |         ffi::duk_put_prop_string(ctx, -2, ERROR_KEY.as_ptr());
    |                                           ^^^^^^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/util.rs:260:37
    |
260 |     ffi::duk_put_global_string(ctx, UDATA.as_ptr());
    |                                     ^^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/util.rs:264:37
    |
264 |     ffi::duk_put_global_string(ctx, ANYMAP.as_ptr());
    |                                     ^^^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/util.rs:276:37
    |
276 |     ffi::duk_get_global_string(ctx, UDATA.as_ptr());
    |                                     ^^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/util.rs:283:37
    |
283 |     ffi::duk_get_global_string(ctx, ANYMAP.as_ptr());
    |                                     ^^^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/ducc.rs:367:50
    |
367 |             ffi::duk_get_global_string(self.ctx, STASH_KEY.as_ptr());
    |                                                  ^^^^^^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/ducc.rs:390:50
    |
390 |             ffi::duk_put_global_string(self.ctx, STASH_KEY.as_ptr());
    |                                                  ^^^^^^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/function.rs:130:52
    |
130 |             ffi::duk_put_prop_string(ducc.ctx, -2, FUNC.as_ptr());
    |                                                    ^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
  --> /build/texlab-1.7.0-vendor/ducc/src/function.rs:86:47
   |
86 |             ffi::duk_get_prop_string(ctx, -1, FUNC.as_ptr());
   |                                               ^^^^^^^^^^^^^ expected u8, found i8
   |
   = note: expected type `*const u8`
              found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/function.rs:117:42
    |
117 |         ffi::duk_get_prop_string(ctx, 0, FUNC.as_ptr());
    |                                          ^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error[E0308]: mismatched types
   --> /build/texlab-1.7.0-vendor/ducc/src/function.rs:121:42
    |
121 |         ffi::duk_put_prop_string(ctx, 0, FUNC.as_ptr());
    |                                          ^^^^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`
error: aborting due to 15 previous errors
For more information about this error, 

full log: https://logs.nix.ci/?key=nixos/nixpkgs.73839&attempt_id=c7ed5679-f27e-4199-adbd-cb2100515246

Can we mutate userdata entry inside a JS invoked function?

@SkylerLipthay Hi! Is there a way to set userdata inside a JS invoked function. Thanks

fn set_name(inv: Invocation) -> Result<Value, DuccError>{
    let this = inv.this.as_object().unwrap();
    let engine = inv.ducc;

    engine.set_user_data("name", "Ducc Rocks!");
    //Compile error here: cannot borrow `*engine` as mutable, as it is behind a `&` reference

    return Ok(Value::Boolean(true));
}

Idea: object (and function?) handle

Hi! While I've gotten used to storing everything in ducc.globals(), I still get a bit annoyed at the whole retrieve everything from globals every time. For instance, for many calls crossing the Rust<->JS boundary I'll have to do something like ducc.globals().get("namespace").get("library").get("_refs").get(index).

However, I got an idea for a potential way to kind of store references to Duktape for faster and nicer retrieval, without sacrificing any safety guarantees. Basically, it'd be a handle type looking something like this:

#[derive(Clone)]
struct ObjectHandle {
  rc: Rc<ffi::duk_uarridx_t>
}
impl ObjectHandle {
  pub fn get_object(ducc: &Ducc) -> Object {
    // return object from heap stash
  } 
}

and API for retrieving an ObjectHandle from an Object somewhere.

Some practical concerns would be making sure that GC doesn't remove our object when we hold a handle (but is able to if we don't), which depends on Duktape's support for it. One scary bit is Drop for ObjectHandle; how can we inform Duktape that it's okay to GC the referenced object without access to &Ducc? Maybe a drop_object_reference(&Ducc) that must be called before Drop or it will panic.

The benefits of this approach would be the ability to keep a reference certain objects without caring about what is happening in the JS world, and I think the ergonomics of using it are better than the global referencing as well. If this works, it could be extended to FunctionHandle etc as well.

Does this sound like a viable/safe idea? If it turns out well, maybe the same approach would work with Mini-V8 as well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.