Skip to content

Commit

Permalink
url: add ToObject method to native URL class
Browse files Browse the repository at this point in the history
Provides a factory method to convert a native URL class
into a JS URL object.

```c++
Environment* env = ...

URL url("http://example.org/a/b/c?query#fragment");

MaybeLocal<Value> val = url.ToObject(env);
```

PR-URL: #12507
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
jasnell authored and evanlucas committed May 1, 2017
1 parent 4b6097d commit c7de98a
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 17 deletions.
4 changes: 4 additions & 0 deletions lib/internal/bootstrap_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@

_process.setupRawDebug();

// Ensure setURLConstructor() is called before the native
// URL::ToObject() method is used.
NativeModule.require('internal/url');

Object.defineProperty(process, 'argv0', {
enumerable: true,
configurable: false,
Expand Down
25 changes: 25 additions & 0 deletions lib/internal/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -1400,6 +1400,31 @@ function getPathFromURL(path) {
return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path);
}

function NativeURL(ctx) {
this[context] = ctx;
}
NativeURL.prototype = URL.prototype;

function constructUrl(flags, protocol, username, password,
host, port, path, query, fragment) {
var ctx = new URLContext();
ctx.flags = flags;
ctx.scheme = protocol;
ctx.username = username;
ctx.password = password;
ctx.port = port;
ctx.path = path;
ctx.query = query;
ctx.fragment = fragment;
ctx.host = host;
const url = new NativeURL(ctx);
url[searchParams] = new URLSearchParams();
url[searchParams][context] = url;
initSearchParams(url[searchParams], query);
return url;
}
binding.setURLConstructor(constructUrl);

module.exports = {
toUSVString,
getPathFromURL,
Expand Down
1 change: 1 addition & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ namespace node {
V(tls_wrap_constructor_template, v8::FunctionTemplate) \
V(tty_constructor_template, v8::FunctionTemplate) \
V(udp_constructor_function, v8::Function) \
V(url_constructor_function, v8::Function) \
V(write_wrap_constructor_function, v8::Function) \

class Environment;
Expand Down
96 changes: 79 additions & 17 deletions src/node_url.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ using v8::HandleScope;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::MaybeLocal;
using v8::Null;
using v8::Object;
using v8::String;
using v8::TryCatch;
using v8::Undefined;
using v8::Value;

Expand Down Expand Up @@ -1226,6 +1228,29 @@ namespace url {
}
}

static inline void SetArgs(Environment* env,
Local<Value> argv[],
const struct url_data* url) {
Isolate* isolate = env->isolate();
argv[ARG_FLAGS] = Integer::NewFromUnsigned(isolate, url->flags);
if (url->flags & URL_FLAGS_HAS_SCHEME)
argv[ARG_PROTOCOL] = OneByteString(isolate, url->scheme.c_str());
if (url->flags & URL_FLAGS_HAS_USERNAME)
argv[ARG_USERNAME] = UTF8STRING(isolate, url->username);
if (url->flags & URL_FLAGS_HAS_PASSWORD)
argv[ARG_PASSWORD] = UTF8STRING(isolate, url->password);
if (url->flags & URL_FLAGS_HAS_HOST)
argv[ARG_HOST] = UTF8STRING(isolate, url->host);
if (url->flags & URL_FLAGS_HAS_QUERY)
argv[ARG_QUERY] = UTF8STRING(isolate, url->query);
if (url->flags & URL_FLAGS_HAS_FRAGMENT)
argv[ARG_FRAGMENT] = UTF8STRING(isolate, url->fragment);
if (url->port > -1)
argv[ARG_PORT] = Integer::New(isolate, url->port);
if (url->flags & URL_FLAGS_HAS_PATH)
argv[ARG_PATH] = Copy(env, url->path);
}

static void Parse(Environment* env,
Local<Value> recv,
const char* input,
Expand Down Expand Up @@ -1267,23 +1292,7 @@ namespace url {
undef,
undef,
};
argv[ARG_FLAGS] = Integer::NewFromUnsigned(isolate, url.flags);
if (url.flags & URL_FLAGS_HAS_SCHEME)
argv[ARG_PROTOCOL] = OneByteString(isolate, url.scheme.c_str());
if (url.flags & URL_FLAGS_HAS_USERNAME)
argv[ARG_USERNAME] = UTF8STRING(isolate, url.username);
if (url.flags & URL_FLAGS_HAS_PASSWORD)
argv[ARG_PASSWORD] = UTF8STRING(isolate, url.password);
if (url.flags & URL_FLAGS_HAS_HOST)
argv[ARG_HOST] = UTF8STRING(isolate, url.host);
if (url.flags & URL_FLAGS_HAS_QUERY)
argv[ARG_QUERY] = UTF8STRING(isolate, url.query);
if (url.flags & URL_FLAGS_HAS_FRAGMENT)
argv[ARG_FRAGMENT] = UTF8STRING(isolate, url.fragment);
if (url.port > -1)
argv[ARG_PORT] = Integer::New(isolate, url.port);
if (url.flags & URL_FLAGS_HAS_PATH)
argv[ARG_PATH] = Copy(env, url.path);
SetArgs(env, argv, &url);
(void)cb->Call(context, recv, arraysize(argv), argv);
} else if (error_cb->IsFunction()) {
Local<Value> argv[2] = { undef, undef };
Expand Down Expand Up @@ -1418,6 +1427,58 @@ namespace url {
v8::NewStringType::kNormal).ToLocalChecked());
}

// This function works by calling out to a JS function that creates and
// returns the JS URL object. Be mindful of the JS<->Native boundary
// crossing that is required.
const Local<Value> URL::ToObject(Environment* env) const {
Isolate* isolate = env->isolate();
Local<Context> context = env->context();
HandleScope handle_scope(isolate);
Context::Scope context_scope(context);

const Local<Value> undef = Undefined(isolate);

if (context_.flags & URL_FLAGS_FAILED)
return Local<Value>();

Local<Value> argv[9] = {
undef,
undef,
undef,
undef,
undef,
undef,
undef,
undef,
undef,
};
SetArgs(env, argv, &context_);

TryCatch try_catch(isolate);

// The SetURLConstructor method must have been called already to
// set the constructor function used below. SetURLConstructor is
// called automatically when the internal/url.js module is loaded
// during the internal/bootstrap_node.js processing.
MaybeLocal<Value> ret =
env->url_constructor_function()
->Call(env->context(), undef, 9, argv);

if (ret.IsEmpty()) {
ClearFatalExceptionHandlers(env);
FatalException(isolate, try_catch);
}

return ret.ToLocalChecked();
}

static void SetURLConstructor(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK_EQ(args.Length(), 1);
CHECK(args[0]->IsFunction());
env->set_url_constructor_function(args[0].As<Function>());
}

static void Init(Local<Object> target,
Local<Value> unused,
Local<Context> context,
Expand All @@ -1428,6 +1489,7 @@ namespace url {
env->SetMethod(target, "toUSVString", ToUSVString);
env->SetMethod(target, "domainToASCII", DomainToASCII);
env->SetMethod(target, "domainToUnicode", DomainToUnicode);
env->SetMethod(target, "setURLConstructor", SetURLConstructor);

#define XX(name, _) NODE_DEFINE_CONSTANT(target, name);
FLAGS(XX)
Expand Down
9 changes: 9 additions & 0 deletions src/node_url.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include "node.h"
#include "env.h"
#include "env-inl.h"

#include <string>

namespace node {
namespace url {

using v8::Local;
using v8::Value;


#define BIT_AT(a, i) \
(!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
(1 << ((unsigned int) (i) & 7))))
Expand Down Expand Up @@ -619,6 +626,8 @@ class URL {
return ret;
}

const Local<Value> ToObject(Environment* env) const;

private:
struct url_data context_;
};
Expand Down

0 comments on commit c7de98a

Please sign in to comment.