Skip to content

Commit

Permalink
Finishes up the variable dec hoisting. Also fixes typeof issue.
Browse files Browse the repository at this point in the history
- Caveat: the for-in loop variant with a var declaration is now broken.
  It can't properly read the ident because of the transformation to
  the var dec node by the hoisting mechanism. There are also other issues
  with for-in loops, so this is next on the agenda.
- Earlier commit introduced ReferenceError when referencing undefined
  variable. typeof operator does not throw the error, updated to handle
  this case.
  • Loading branch information
ndreynolds committed Nov 12, 2012
1 parent dc3155b commit 3154540
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 53 deletions.
45 changes: 25 additions & 20 deletions src/eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,22 +109,26 @@ void
fh_var_dec_scan(js_val *ctx, ast_node *node)
{
// Sweep for variable declarations
ast_node *child;
while (!node->visited) {
child = pop_node(node);
if (child->type == NODE_VAR_DEC) {
// Do the variable declaration, ignoring the rval.
fh_var_dec(ctx, child, true);
// Now change it to an assignment.
child->type = NODE_ASGN;
child->sval = "=";

// Don't touch functions (stay within our current scope)
if (node->type == NODE_FUNC) return;

// Declare the variable and possibly convert it to an assignment
if (node->type == NODE_VAR_DEC) {
fh_var_dec(ctx, node, true); // true to ignore rhs
if (node->e2) {
node->type = NODE_ASGN;
node->sval = "=";
}
if (child->type == NODE_VAR_DEC_LST) {
while (!child->visited) fh_var_dec_scan(ctx, pop_node(child));
rewind_node(child);
else {
node->type = NODE_EMPT_STMT;
}
}
rewind_node(node);

// Recurse sub nodes, will hit the whole tree.
if (node->e1) fh_var_dec_scan(ctx, node->e1);
if (node->e2) fh_var_dec_scan(ctx, node->e2);
if (node->e3) fh_var_dec_scan(ctx, node->e3);
}

js_val *
Expand Down Expand Up @@ -370,12 +374,10 @@ fh_forin(js_val *ctx, ast_node *node)
*proto = fh_eval(ctx, node->e2),
*name = fh_str_from_node(ctx, node->e1);

// If variable declaration:
if (node->e1->type == NODE_VAR_DEC) {
fh_eval(ctx, node->e1);
name = JSSTR(node->e1->e1->sval);
}

// FIXME:
// - Doesn't get name out of hoisted variable declaration.
// - Doesn't properly handle member expressions and other permitted lhs hijinks

// Note that during the first iteration, the prototype is the object.
while (proto != NULL) {
OBJ_ITER(proto, p) {
Expand Down Expand Up @@ -631,8 +633,11 @@ fh_prefix_exp(js_val *ctx, ast_node *node)

if (STREQ(op, "delete"))
return fh_delete(ctx, node);
if (STREQ(op, "typeof"))
if (STREQ(op, "typeof")) {
if (node->e1->type == NODE_IDENT)
return JSSTR(fh_typeof(fh_get_rec(ctx, node->e1->sval)));
return JSSTR(fh_typeof(fh_eval(ctx, node->e1)));
}
if (STREQ(op, "void")) {
fh_eval(ctx, node->e1);
return JSUNDEF();
Expand Down
1 change: 1 addition & 0 deletions src/flathead.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ typedef enum {
P_WRITE = 0x01,
P_ENUM = 0x02,
P_CONF = 0x04,
P_NONE = 0x08,
P_BUILTIN = P_WRITE | P_CONF,
P_DEFAULT = P_WRITE | P_ENUM | P_CONF
} js_prop_flags;
Expand Down
57 changes: 25 additions & 32 deletions src/props.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@
// Get a property
// ----------------------------------------------------------------------------

/**
* Lookup a property on an object, resolve the value, and return it.
*/
/* Lookup a property on an object, resolve the value, and return it. */
js_val *
fh_get(js_val *obj, char *name)
{
Expand All @@ -37,19 +35,15 @@ fh_get(js_val *obj, char *name)
return prop ? prop->ptr : JSUNDEF();
}

/**
* Same as `fh_get`, but recurse the scope chain.
*/
/* Same as `fh_get`, but recurse the scope chain. */
js_val *
fh_get_rec(js_val *obj, char *name)
{
js_prop *prop = fh_get_prop_rec(obj, name);
return prop ? prop->ptr : JSUNDEF();
}

/**
* Same as `fh_get`, but recurse the prototype chain (if one exists).
*/
/* Same as `fh_get`, but recurse the prototype chain (if one exists). */
js_val *
fh_get_proto(js_val *obj, char *name)
{
Expand All @@ -62,9 +56,7 @@ fh_get_proto(js_val *obj, char *name)
return val;
}

/**
* Lookup a property on an object and return it.
*/
/* Lookup a property on an object and return it. */
js_prop *
fh_get_prop(js_val *obj, char *name)
{
Expand Down Expand Up @@ -97,46 +89,49 @@ fh_get_prop_proto(js_val *obj, char *name)
// Set a property
// ----------------------------------------------------------------------------

/**
* Set a property on an object using the provided name and value, and the
/* Set a property on an object using the provided name and value, and the
* default property flags.
*/
void
fh_set(js_val *obj, char *name, js_val *val)
{
fh_set_prop(obj, name, val, P_DEFAULT);
fh_set_prop(obj, name, val, P_NONE);
}

/**
* Set a property on an object using the provided name, value, and property
* flags.
/* Set a property on an object using the provided name, value, and property
* flags.
*/
void
fh_set_prop(js_val *obj, char *name, js_val *val, js_prop_flags flags)
{
bool add = false;
// Get the existing prop or create a new one.
bool new = false;
js_prop *prop = fh_get_prop(obj, name);
if (prop == NULL) {
prop = fh_new_prop(P_DEFAULT);
add = true;
new = true;
}

prop->writable = flags & P_WRITE;
prop->configurable = flags & P_CONF;
prop->enumerable = flags & P_ENUM;
// Update the prop flags.
// The P_NONE value is used to convey that the flags should not be changed.
if (flags != P_NONE) {
prop->writable = flags & P_WRITE;
prop->configurable = flags & P_CONF;
prop->enumerable = flags & P_ENUM;
}

prop->name = malloc((strlen(name) + 1) * sizeof(char));
strcpy(prop->name, name);
prop->ptr = val;
prop->circular = prop->ptr == obj ? 1 : 0; // Do we have a circular reference?

// Don't add if it already exists (bad things happen).
if (add)
// Add the prop if new
if (new) {
prop->name = malloc((strlen(name) + 1) * sizeof(char));
strcpy(prop->name, name);
HASH_ADD_KEYPTR(hh, obj->map, prop->name, strlen(prop->name), prop);
}
}

/*
* Set a property on the given object, or -- if not defined -- the closest
/* Set a property on the given object, or -- if not defined -- the closest
* parent scope on which the name is already defined.
*/
void
Expand All @@ -163,9 +158,7 @@ fh_set_rec(js_val *obj, char *name, js_val *val)
// Delete a property
// ----------------------------------------------------------------------------

/**
* Find and delete a property from an object by name.
*/
/* Find and delete a property from an object by name. */
bool
fh_del_prop(js_val *obj, char *name)
{
Expand Down
6 changes: 6 additions & 0 deletions test/test_arguments.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,9 @@ var assert = console.assert;
assert(arguments[1] === 2);
assert(arguments[2] === 3);
})(1, 2, 3);


// Parameter named 'arguments' overwrites the arguments object.
(function(arguments) {
assert(typeof arguments == 'number');
})(42);
2 changes: 2 additions & 0 deletions test/test_delete.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
var assert = console.assert;

var assertEquals = function(a, b) {
if (a !== b)
console.log(a + ' !== ' + b);
assert(a === b);
};

Expand Down
2 changes: 2 additions & 0 deletions test/test_forin.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ var assert = console.assert;
// Basic for-in
var x = 100; // expect x to be redeclared
var obj = {a:1, b:2, c:3};
console.log(x);
for(var x in obj) {
console.log(x);
assert(obj[x] > 0 && obj[x] < 4);
}
2 changes: 1 addition & 1 deletion test/test_object_global.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ test('Object.create()', function() {
assert(newObj.baz === 'goodbye');
});

var obj = { a: 1, b: 2, c:3 };
var obj = { a: 1, b: 2, c: 3 };

test('Object.defineProperty(obj, descriptor)', function() {
// This one isn't enumerable.
Expand Down
5 changes: 5 additions & 0 deletions test/test_strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ assert(s1[6] === 'W');
assert(s1[11] === '!');
assert(s1[12] === undefined);
assert(s1[1000] === undefined);

// Direct operations on strings

assert('abc'[1] === 'b');
assert('abc'.toString() === 'abc');

0 comments on commit 3154540

Please sign in to comment.