This commit is contained in:
Robert Sayre 2010-05-26 10:22:33 -07:00
Родитель 0344e81821 19c8685e55
Коммит b925418444
11 изменённых файлов: 587 добавлений и 292 удалений

Просмотреть файл

@ -68,6 +68,7 @@ function my_load(filename) {
}
my_load('jsdefs.js');
my_load('jslex.js');
my_load('jsparse.js');
my_load('jsexec.js');

Просмотреть файл

@ -42,8 +42,6 @@
* separately to take advantage of the simple switch-case constant propagation
* done by SpiderMonkey.
*/
const GLOBAL = this;
var tokens = [
// End of source.
"END",
@ -95,9 +93,9 @@ var tokens = [
];
// Operator and punctuator mapping from token to tree node type name.
// NB: superstring tokens (e.g., ++) must come before their substring token
// counterparts (+ in the example), so that the opRegExp regular expression
// synthesized from this list makes the longest possible match.
// NB: because the lexer doesn't backtrack, all token prefixes must themselves
// be valid tokens (e.g. !== is acceptable because its prefixes are the valid
// tokens != and !).
var opTypeNames = {
'\n': "NEWLINE",
';': "SEMICOLON",
@ -144,18 +142,21 @@ var opTypeNames = {
var keywords = {__proto__: null};
// Define const END, etc., based on the token names. Also map name to index.
var tokenIds = {};
var consts = "const ";
for (var i = 0, j = tokens.length; i < j; i++) {
if (i > 0)
consts += ", ";
var t = tokens[i];
var name;
if (/^[a-z]/.test(t)) {
consts += t.toUpperCase();
name = t.toUpperCase();
keywords[t] = i;
} else {
consts += (/^\W/.test(t) ? opTypeNames[t] : t);
name = (/^\W/.test(t) ? opTypeNames[t] : t);
}
consts += " = " + i;
consts += name + " = " + i;
tokenIds[name] = i;
tokens[t] = i;
}
eval(consts + ";");

Просмотреть файл

@ -110,9 +110,9 @@ var global = {
var s = {object: global, parent: null};
return new FunctionObject(f, s);
},
Array: function Array(dummy) {
Array: function (dummy) {
// Array when called as a function acts as a constructor.
return GLOBAL.Array.apply(this, arguments);
return Array.apply(this, arguments);
},
String: function String(s) {
// Called as function or constructor: convert argument to string type.

430
js/narcissus/jslex.js Normal file
Просмотреть файл

@ -0,0 +1,430 @@
/* vim: set sw=4 ts=8 et tw=78: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Narcissus JavaScript engine.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>.
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* Narcissus - JS implemented in JS.
*
* Lexical scanner.
*/
// Build up a trie of operator tokens.
var opTokens = {};
for (var op in opTypeNames) {
if (op === '\n' || op === '.')
continue;
var node = opTokens;
for (var i = 0; i < op.length; i++) {
var ch = op[i];
if (!(ch in node))
node[ch] = {};
node = node[ch];
node.op = op;
}
}
function Tokenizer(s, f, l) {
this.cursor = 0;
this.source = String(s);
this.tokens = [];
this.tokenIndex = 0;
this.lookahead = 0;
this.scanNewlines = false;
this.scanOperand = true;
this.filename = f || "";
this.lineno = l || 1;
}
Tokenizer.prototype = {
get done() {
return this.peek() == END;
},
get token() {
return this.tokens[this.tokenIndex];
},
match: function (tt) {
return this.get() == tt || this.unget();
},
mustMatch: function (tt) {
if (!this.match(tt))
throw this.newSyntaxError("Missing " + tokens[tt].toLowerCase());
return this.token;
},
peek: function () {
var tt, next;
if (this.lookahead) {
next = this.tokens[(this.tokenIndex + this.lookahead) & 3];
tt = (this.scanNewlines && next.lineno != this.lineno)
? NEWLINE
: next.type;
} else {
tt = this.get();
this.unget();
}
return tt;
},
peekOnSameLine: function () {
this.scanNewlines = true;
var tt = this.peek();
this.scanNewlines = false;
return tt;
},
// Eats comments and whitespace.
skip: function () {
var input = this.source;
for (;;) {
var ch = input[this.cursor++];
var next = input[this.cursor];
if (ch === '\n' && !this.scanNewlines) {
this.lineno++;
} else if (ch === '/' && next === '*') {
this.cursor++;
for (;;) {
ch = input[this.cursor++];
if (ch === undefined)
throw this.newSyntaxError("Unterminated comment");
if (ch === '*') {
next = input[this.cursor];
if (next === '/') {
this.cursor++;
break;
}
} else if (ch === '\n') {
this.lineno++;
}
}
} else if (ch === '/' && next === '/') {
this.cursor++;
for (;;) {
ch = input[this.cursor++];
if (ch === undefined)
return;
if (ch === '\n') {
this.lineno++;
break;
}
}
} else if (ch !== ' ' && ch !== '\t') {
this.cursor--;
return;
}
}
},
// Lexes the exponential part of a number, if present. Returns true iff an
// exponential part was found.
lexExponent: function() {
var input = this.source;
var next = input[this.cursor];
if (next === 'e' || next === 'E') {
this.cursor++;
ch = input[this.cursor++];
if (ch === '+' || ch === '-')
ch = input[this.cursor++];
if (ch < '0' || ch > '9')
throw this.newSyntaxError("Missing exponent");
do {
ch = input[this.cursor++];
} while (ch >= '0' && ch <= '9');
this.cursor--;
return true;
}
return false;
},
lexZeroNumber: function (ch) {
var token = this.token, input = this.source;
token.type = NUMBER;
ch = input[this.cursor++];
if (ch === '.') {
do {
ch = input[this.cursor++];
} while (ch >= '0' && ch <= '9');
this.cursor--;
this.lexExponent();
token.value = parseFloat(token.start, this.cursor);
} else if (ch === 'x' || ch === 'X') {
do {
ch = input[this.cursor++];
} while ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
(ch >= 'A' && ch <= 'F'));
this.cursor--;
token.value = parseInt(input.substring(token.start, this.cursor));
} else if (ch >= '0' && ch <= '7') {
do {
ch = input[this.cursor++];
} while (ch >= '0' && ch <= '7');
this.cursor--;
token.value = parseInt(input.substring(token.start, this.cursor));
} else {
this.cursor--;
this.lexExponent(); // 0E1, &c.
token.value = 0;
}
},
lexNumber: function (ch) {
var token = this.token, input = this.source;
token.type = NUMBER;
var floating = false;
do {
ch = input[this.cursor++];
if (ch === '.' && !floating) {
floating = true;
ch = input[this.cursor++];
}
} while (ch >= '0' && ch <= '9');
this.cursor--;
var exponent = this.lexExponent();
floating = floating || exponent;
var str = input.substring(token.start, this.cursor);
token.value = floating ? parseFloat(str) : parseInt(str);
},
lexDot: function (ch) {
var token = this.token, input = this.source;
var next = input[this.cursor];
if (next >= '0' && next <= '9') {
do {
ch = input[this.cursor++];
} while (ch >= '0' && ch <= '9');
this.cursor--;
this.lexExponent();
token.type = NUMBER;
token.value = parseFloat(token.start, this.cursor);
} else {
token.type = DOT;
token.assignOp = null;
token.value = '.';
}
},
lexString: function (ch) {
var token = this.token, input = this.source;
token.type = STRING;
var hasEscapes = false;
var delim = ch;
ch = input[this.cursor++];
while (ch !== delim) {
if (ch === '\\') {
hasEscapes = true;
this.cursor++;
}
ch = input[this.cursor++];
}
token.value = (hasEscapes)
? eval(input.substring(token.start, this.cursor))
: input.substring(token.start + 1, this.cursor - 1);
},
lexRegExp: function (ch) {
var token = this.token, input = this.source;
token.type = REGEXP;
do {
ch = input[this.cursor++];
if (ch === '\\') {
this.cursor++;
} else if (ch === '[') {
do {
if (ch === undefined)
throw this.newSyntaxError("Unterminated character class");
if (ch === '\\')
this.cursor++;
ch = input[this.cursor++];
} while (ch !== ']');
} else if (ch === undefined) {
throw this.newSyntaxError("Unterminated regex");
}
} while (ch !== '/');
do {
ch = input[this.cursor++];
} while (ch >= 'a' && ch <= 'z');
this.cursor--;
token.value = eval(input.substring(token.start, this.cursor));
},
lexOp: function (ch) {
var token = this.token, input = this.source;
// A bit ugly, but it seems wasteful to write a trie lookup routine for
// only 3 characters...
var node = opTokens[ch];
var next = input[this.cursor];
if (next in node) {
node = node[next];
this.cursor++;
next = input[this.cursor];
if (next in node) {
node = node[next];
this.cursor++;
next = input[this.cursor];
}
}
var op = node.op;
if (assignOps[op] && input[this.cursor] === '=') {
this.cursor++;
token.type = ASSIGN;
token.assignOp = tokenIds[opTypeNames[op]];
op += '=';
} else {
token.type = tokenIds[opTypeNames[op]];
if (this.scanOperand) {
switch (token.type) {
case PLUS: token.type = UNARY_PLUS; break;
case MINUS: token.type = UNARY_MINUS; break;
}
}
token.assignOp = null;
}
token.value = op;
},
// FIXME: Unicode escape sequences
// FIXME: Unicode identifiers
lexIdent: function (ch) {
var token = this.token, input = this.source;
do {
ch = input[this.cursor++];
} while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') || ch === '$' || ch === '_');
this.cursor--; // Put the non-word character back.
var id = input.substring(token.start, this.cursor);
token.type = keywords[id] || IDENTIFIER;
token.value = id;
},
get: function () {
var token;
while (this.lookahead) {
--this.lookahead;
this.tokenIndex = (this.tokenIndex + 1) & 3;
token = this.tokens[this.tokenIndex];
if (token.type != NEWLINE || this.scanNewlines)
return token.type;
}
this.skip();
this.tokenIndex = (this.tokenIndex + 1) & 3;
token = this.tokens[this.tokenIndex];
if (!token)
this.tokens[this.tokenIndex] = token = {};
var input = this.source;
if (this.cursor === input.length)
return token.type = END;
token.start = this.cursor;
token.lineno = this.lineno;
var ch = input[this.cursor++];
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
ch === '$' || ch === '_') {
this.lexIdent(ch);
} else if (this.scanOperand && ch === '/') {
this.lexRegExp(ch);
} else if (ch in opTokens) {
this.lexOp(ch);
} else if (ch === '.') {
this.lexDot(ch);
} else if (ch >= '1' && ch <= '9') {
this.lexNumber(ch);
} else if (ch === '0') {
this.lexZeroNumber(ch);
} else if (ch === '"' || ch === "'") {
this.lexString(ch);
} else if (this.scanNewlines && ch === '\n') {
token.type = NEWLINE;
token.value = '\n';
this.lineno++;
} else {
throw this.newSyntaxError("Illegal token");
}
token.end = this.cursor;
return token.type;
},
unget: function () {
if (++this.lookahead == 4) throw "PANIC: too much lookahead!";
this.tokenIndex = (this.tokenIndex - 1) & 3;
},
newSyntaxError: function (m) {
var e = new SyntaxError(m, this.filename, this.lineno);
e.source = this.source;
e.cursor = this.cursor;
return e;
}
};

Просмотреть файл

@ -38,179 +38,9 @@
/*
* Narcissus - JS implemented in JS.
*
* Lexical scanner and parser.
* Parser.
*/
// Build a regexp that recognizes operators and punctuators (except newline).
var opRegExpSrc = "^";
for (i in opTypeNames) {
if (i == '\n')
continue;
if (opRegExpSrc != "^")
opRegExpSrc += "|^";
opRegExpSrc += i.replace(/[?|^&(){}\[\]+\-*\/\.]/g, "\\$&");
}
var opRegExp = new RegExp(opRegExpSrc);
// A regexp to match floating point literals (but not integer literals).
var fpRegExp = /^\d+\.\d*(?:[eE][-+]?\d+)?|^\d+(?:\.\d*)?[eE][-+]?\d+|^\.\d+(?:[eE][-+]?\d+)?/;
// A regexp to match regexp literals.
var reRegExp = /^\/((?:\\.|\[(?:\\.|[^\]])*\]|[^\/])+)\/([gimy]*)/;
function Tokenizer(s, f, l) {
this.cursor = 0;
this.source = String(s);
this.tokens = [];
this.tokenIndex = 0;
this.lookahead = 0;
this.scanNewlines = false;
this.scanOperand = true;
this.filename = f || "";
this.lineno = l || 1;
}
Tokenizer.prototype = {
get input() {
return this.source.substring(this.cursor);
},
get done() {
return this.peek() == END;
},
get token() {
return this.tokens[this.tokenIndex];
},
match: function (tt) {
return this.get() == tt || this.unget();
},
mustMatch: function (tt) {
if (!this.match(tt))
throw this.newSyntaxError("Missing " + tokens[tt].toLowerCase());
return this.token;
},
peek: function () {
var tt, next;
if (this.lookahead) {
next = this.tokens[(this.tokenIndex + this.lookahead) & 3];
if (this.scanNewlines && next.lineno != this.lineno)
tt = NEWLINE;
else
tt = next.type;
} else {
tt = this.get();
this.unget();
}
return tt;
},
peekOnSameLine: function () {
this.scanNewlines = true;
var tt = this.peek();
this.scanNewlines = false;
return tt;
},
get: function () {
var token;
while (this.lookahead) {
--this.lookahead;
this.tokenIndex = (this.tokenIndex + 1) & 3;
token = this.tokens[this.tokenIndex];
if (token.type != NEWLINE || this.scanNewlines)
return token.type;
}
for (;;) {
var input = this.input;
var match = (this.scanNewlines ? /^[ \t]+/ : /^\s+/)(input);
if (match) {
var spaces = match[0];
this.cursor += spaces.length;
var newlines = spaces.match(/\n/g);
if (newlines)
this.lineno += newlines.length;
input = this.input;
}
if (!(match = /^\/(?:\*(?:.|\n)*?\*\/|\/.*)/(input)))
break;
var comment = match[0];
this.cursor += comment.length;
newlines = comment.match(/\n/g);
if (newlines)
this.lineno += newlines.length
}
this.tokenIndex = (this.tokenIndex + 1) & 3;
token = this.tokens[this.tokenIndex];
if (!token)
this.tokens[this.tokenIndex] = token = {};
if (!input)
return token.type = END;
if ((match = fpRegExp(input))) {
token.type = NUMBER;
token.value = parseFloat(match[0]);
} else if ((match = /^0[xX][\da-fA-F]+|^0[0-7]*|^\d+/(input))) {
token.type = NUMBER;
token.value = parseInt(match[0]);
} else if ((match = /^[$_\w]+/(input))) { // FIXME no ES3 unicode
var id = match[0];
token.type = keywords[id] || IDENTIFIER;
token.value = id;
} else if ((match = /^"(?:\\.|[^"])*"|^'(?:\\.|[^'])*'/(input))) { //"){
token.type = STRING;
token.value = eval(match[0]);
} else if (this.scanOperand && (match = reRegExp(input))) {
token.type = REGEXP;
token.value = new RegExp(match[1], match[2]);
} else if ((match = opRegExp(input))) {
var op = match[0];
if (assignOps[op] && input[op.length] == '=') {
token.type = ASSIGN;
token.assignOp = GLOBAL[opTypeNames[op]];
match[0] += '=';
} else {
token.type = GLOBAL[opTypeNames[op]];
if (this.scanOperand &&
(token.type == PLUS || token.type == MINUS)) {
token.type += UNARY_PLUS - PLUS;
}
token.assignOp = null;
}
token.value = op;
} else if (this.scanNewlines && (match = /^\n/(input))) {
token.type = NEWLINE;
} else {
throw this.newSyntaxError("Illegal token");
}
token.start = this.cursor;
this.cursor += match[0].length;
token.end = this.cursor;
token.lineno = this.lineno;
return token.type;
},
unget: function () {
if (++this.lookahead == 4) throw "PANIC: too much lookahead!";
this.tokenIndex = (this.tokenIndex - 1) & 3;
},
newSyntaxError: function (m) {
var e = new SyntaxError(m, this.filename, this.lineno);
e.source = this.source;
e.cursor = this.cursor;
return e;
}
};
function CompilerContext(inFunction) {
this.inFunction = inFunction;
this.stmtStack = [];
@ -690,7 +520,7 @@ var opPrecedence = {
// Map operator type code to precedence.
for (i in opPrecedence)
opPrecedence[GLOBAL[i]] = opPrecedence[i];
opPrecedence[tokenIds[i]] = opPrecedence[i];
var opArity = {
COMMA: -2,
@ -715,7 +545,7 @@ var opArity = {
// Map operator type code to arity.
for (i in opArity)
opArity[GLOBAL[i]] = opArity[i];
opArity[tokenIds[i]] = opArity[i];
function Expression(t, x, stop) {
var n, id, tt, operators = [], operands = [];

Просмотреть файл

@ -2248,6 +2248,11 @@ class AutoValueRooter : private AutoGCRooter
JS_GUARD_OBJECT_NOTIFIER_INIT;
}
void set(jsval v) {
JS_ASSERT(tag == JSVAL);
val = v;
}
void setObject(JSObject *obj) {
JS_ASSERT(tag == JSVAL);
val = OBJECT_TO_JSVAL(obj);

Просмотреть файл

@ -159,7 +159,7 @@ class HashTable : AllocPolicy
return cur == end;
}
const T &front() const {
T &front() const {
JS_ASSERT(!empty());
return cur->t;
}

Просмотреть файл

@ -248,8 +248,6 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
jsid id;
JSObject *obj2;
JSProperty *prop;
uintN attrs;
jsval val;
JS_CHECK_RECURSION(cx, return NULL);
@ -279,32 +277,37 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
break;
if (!prop)
continue;
ok = obj2->getAttributes(cx, id, prop, &attrs);
if (ok) {
if (obj2->isNative() &&
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
bool hasGetter, hasSetter;
AutoValueRooter v(cx, JSVAL_VOID);
AutoValueRooter setter(cx, JSVAL_VOID);
if (obj->isNative()) {
JSScopeProperty *sprop = (JSScopeProperty *) prop;
val = JSVAL_VOID;
if (attrs & JSPROP_GETTER)
val = sprop->getterValue();
if (attrs & JSPROP_SETTER) {
if (val != JSVAL_VOID) {
/* Mark the getter, then set val to setter. */
ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
NULL)
!= NULL);
}
val = sprop->setterValue();
}
hasGetter = sprop->hasGetterValue();
hasSetter = sprop->hasSetterValue();
if (hasGetter)
v.set(sprop->getterValue());
if (hasSetter)
setter.set(sprop->setterValue());
JS_UNLOCK_OBJ(cx, obj2);
} else {
ok = obj->getProperty(cx, id, &val);
obj->dropProperty(cx, prop);
hasGetter = hasSetter = false;
}
}
obj2->dropProperty(cx, prop);
if (hasSetter) {
/* Mark the getter, then set val to setter. */
if (hasGetter && !JSVAL_IS_PRIMITIVE(v.value())) {
ok = !!MarkSharpObjects(cx, JSVAL_TO_OBJECT(v.value()), NULL);
if (!ok)
break;
if (!JSVAL_IS_PRIMITIVE(val) &&
!MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
}
v.set(setter.value());
} else if (!hasGetter) {
ok = obj->getProperty(cx, id, v.addr());
if (!ok)
break;
}
if (!JSVAL_IS_PRIMITIVE(v.value()) &&
!MarkSharpObjects(cx, JSVAL_TO_OBJECT(v.value()), NULL)) {
ok = JS_FALSE;
break;
}
@ -873,7 +876,7 @@ obj_toString(JSContext *cx, uintN argc, jsval *vp)
if (!obj)
return JS_FALSE;
if (obj->isProxy()) {
if (!JS_GetProxyObjectClass(cx, obj, &clazz))
if (!GetProxyObjectClass(cx, obj, &clazz))
return false;
} else {
obj = js_GetWrappedObject(cx, obj);
@ -1118,14 +1121,6 @@ obj_eval(JSContext *cx, uintN argc, jsval *vp)
return JS_TRUE;
}
/*
* If the caller is a lightweight function and doesn't have a variables
* object, then we need to provide one for the compiler to stick any
* declared (var) variables into.
*/
if (caller->fun && !caller->callobj && !js_GetCallObject(cx, caller))
return JS_FALSE;
/* Accept an optional trailing argument that overrides the scope object. */
JSObject *scopeobj = NULL;
if (argc >= 2) {
@ -1196,6 +1191,7 @@ obj_eval(JSContext *cx, uintN argc, jsval *vp)
* NB: This means that native callers (who reach this point through
* the C API) must use the two parameter form.
*/
JS_ASSERT_IF(caller->argv, caller->callobj);
scopeobj = callerScopeChain;
}
#endif
@ -3199,6 +3195,14 @@ js_DefineBlockVariable(JSContext *cx, JSObject *obj, jsid id, intN index)
JSScopeProperty::HAS_SHORTID, index, NULL);
}
static size_t
GetObjectSize(JSObject *obj)
{
return (obj->isFunction() && !obj->getPrivate())
? sizeof(JSFunction)
: sizeof(JSObject);
}
/*
* Use this method with extreme caution. It trades the guts of two objects and updates
* scope ownership. This operation is not thread-safe, just as fast array to slow array
@ -3212,11 +3216,17 @@ JSObject::swap(JSObject *other)
bool thisOwns = this->isNative() && scope()->object == this;
bool otherOwns = other->isNative() && other->scope()->object == other;
size_t size = GetObjectSize(this);
JS_ASSERT(size == GetObjectSize(other));
/* Trade the guts of the objects. */
JSObject tmp;
memcpy(&tmp, this, sizeof(JSObject));
memcpy(this, other, sizeof(JSObject));
memcpy(other, &tmp, sizeof(JSObject));
union {
JSFunction fun;
JSObject obj;
} tmp;
memcpy(&tmp, this, size);
memcpy(this, other, size);
memcpy(other, &tmp, size);
/* Fixup scope ownerships. */
if (otherOwns)

Просмотреть файл

@ -52,6 +52,8 @@
using namespace js;
namespace js {
JSProxyHandler::~JSProxyHandler()
{
}
@ -173,6 +175,11 @@ JSProxyHandler::finalize(JSContext *cx, JSObject *proxy)
{
}
void
JSProxyHandler::trace(JSTracer *trc, JSObject *proxy)
{
}
JSNoopProxyHandler::JSNoopProxyHandler(JSObject *obj) : mWrappedObject(obj)
{
}
@ -276,6 +283,13 @@ JSNoopProxyHandler::finalize(JSContext *cx, JSObject *proxy)
delete this;
}
void
JSNoopProxyHandler::trace(JSTracer *trc, JSObject *proxy)
{
if (mWrappedObject)
JS_CALL_OBJECT_TRACER(trc, mWrappedObject, "wrappedObject");
}
void *
JSNoopProxyHandler::family()
{
@ -591,8 +605,8 @@ JSProxy::enumerateOwn(JSContext *cx, JSObject *proxy, JSIdArray **idap)
return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->enumerateOwn(cx, proxy, idap));
}
JS_PUBLIC_API(JSBool)
JS_GetProxyObjectClass(JSContext *cx, JSObject *proxy, const char **namep)
JS_FRIEND_API(JSBool)
GetProxyObjectClass(JSContext *cx, JSObject *proxy, const char **namep)
{
if (!proxy->isProxy()) {
char *bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
@ -712,7 +726,11 @@ proxy_TraceObject(JSTracer *trc, JSObject *obj)
obj->traceProtoAndParent(trc);
JS_CALL_VALUE_TRACER(trc, obj->fslots[JSSLOT_PROXY_HANDLER], "handler");
jsval handler = obj->fslots[JSSLOT_PROXY_HANDLER];
if (!JSVAL_IS_PRIMITIVE(handler))
JS_CALL_OBJECT_TRACER(trc, JSVAL_TO_OBJECT(handler), "handler");
else
((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->trace(trc, obj);
if (obj->isFunctionProxy()) {
JS_CALL_VALUE_TRACER(trc, obj->fslots[JSSLOT_PROXY_CALL], "call");
JS_CALL_VALUE_TRACER(trc, obj->fslots[JSSLOT_PROXY_CONSTRUCT], "construct");
@ -756,7 +774,7 @@ obj_proxy_getObjectOps(JSContext *cx, JSClass *clasp)
return &js_ObjectProxyObjectOps;
}
JS_FRIEND_API(JSClass) js_ObjectProxyClass = {
JS_FRIEND_API(JSClass) ObjectProxyClass = {
"ObjectProxy",
JSCLASS_HAS_RESERVED_SLOTS(3) |
JSCLASS_NEW_ENUMERATE,
@ -835,7 +853,7 @@ fun_proxy_getObjectOps(JSContext *cx, JSClass *clasp)
return &js_FunctionProxyObjectOps;
}
JS_FRIEND_API(JSClass) js_FunctionProxyClass = {
JS_FRIEND_API(JSClass) FunctionProxyClass = {
"FunctionProxy",
JSCLASS_HAS_RESERVED_SLOTS(3) |
JSCLASS_NEW_ENUMERATE,
@ -845,10 +863,10 @@ JS_FRIEND_API(JSClass) js_FunctionProxyClass = {
NULL, NULL, NULL, NULL
};
JS_PUBLIC_API(JSObject *)
JS_NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSString *className)
JS_FRIEND_API(JSObject *)
NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSString *className)
{
JSObject *obj = NewObject(cx, &js_ObjectProxyClass, proto, parent);
JSObject *obj = NewObjectWithGivenProto(cx, &ObjectProxyClass, proto, parent);
if (!obj)
return NULL;
obj->fslots[JSSLOT_PROXY_HANDLER] = handler;
@ -857,11 +875,11 @@ JS_NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *paren
return obj;
}
JS_PUBLIC_API(JSObject *)
JS_NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent,
JS_FRIEND_API(JSObject *)
NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent,
JSObject *call, JSObject *construct)
{
JSObject *obj = NewObject(cx, &js_FunctionProxyClass, proto, parent);
JSObject *obj = NewObjectWithGivenProto(cx, &FunctionProxyClass, proto, parent);
if (!obj)
return NULL;
obj->fslots[JSSLOT_PROXY_HANDLER] = handler;
@ -901,7 +919,7 @@ proxy_create(JSContext *cx, uintN argc, jsval *vp)
parent = JSVAL_TO_OBJECT(vp[0])->getParent();
}
JSString *className = (argc > 2 && JSVAL_IS_STRING(vp[4])) ? JSVAL_TO_STRING(vp[4]) : NULL;
JSObject *proxy = JS_NewObjectProxy(cx, OBJECT_TO_JSVAL(handler), proto, parent, className);
JSObject *proxy = NewObjectProxy(cx, OBJECT_TO_JSVAL(handler), proto, parent, className);
if (!proxy)
return false;
@ -936,7 +954,7 @@ proxy_createFunction(JSContext *cx, uintN argc, jsval *vp)
return false;
}
JSObject *proxy = JS_NewFunctionProxy(cx, OBJECT_TO_JSVAL(handler), proto, parent, call, construct);
JSObject *proxy = NewFunctionProxy(cx, OBJECT_TO_JSVAL(handler), proto, parent, call, construct);
if (!proxy)
return false;
@ -974,7 +992,7 @@ proxy_fix(JSContext *cx, uintN argc, jsval *vp)
return false;
if (obj->isProxy()) {
JSBool flag;
if (!JS_FixProxy(cx, obj, &flag))
if (!FixProxy(cx, obj, &flag))
return false;
*vp = BOOLEAN_TO_JSVAL(flag);
} else {
@ -995,22 +1013,7 @@ static JSFunctionSpec static_methods[] = {
JS_FS_END
};
JS_FRIEND_API(JSObject *)
js_InitProxyClass(JSContext *cx, JSObject *obj)
{
JSObject *module = NewObject(cx, &js_ObjectClass, NULL, obj);
if (!module)
return NULL;
if (!JS_DefineProperty(cx, obj, "Proxy", OBJECT_TO_JSVAL(module),
JS_PropertyStub, JS_PropertyStub, 0)) {
return NULL;
}
if (!JS_DefineFunctions(cx, module, static_methods))
return NULL;
return obj;
}
extern JSClass js_CallableObjectClass;
extern JSClass CallableObjectClass;
static const uint32 JSSLOT_CALLABLE_CALL = JSSLOT_PRIVATE;
static const uint32 JSSLOT_CALLABLE_CONSTRUCT = JSSLOT_PRIVATE + 1;
@ -1019,7 +1022,7 @@ static JSBool
callable_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSObject *callable = JSVAL_TO_OBJECT(argv[-2]);
JS_ASSERT(callable->getClass() == &js_CallableObjectClass);
JS_ASSERT(callable->getClass() == &CallableObjectClass);
jsval fval = callable->fslots[JSSLOT_CALLABLE_CALL];
return js_InternalCall(cx, obj, fval, argc, argv, rval);
}
@ -1028,7 +1031,7 @@ static JSBool
callable_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSObject *callable = JSVAL_TO_OBJECT(argv[-2]);
JS_ASSERT(callable->getClass() == &js_CallableObjectClass);
JS_ASSERT(callable->getClass() == &CallableObjectClass);
jsval fval = callable->fslots[JSSLOT_CALLABLE_CONSTRUCT];
if (fval == JSVAL_VOID) {
/* We don't have an explicit constructor so allocate a new object and use the call. */
@ -1054,7 +1057,7 @@ callable_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval
return js_InternalCall(cx, obj, fval, argc, argv, rval);
}
JSClass js_CallableObjectClass = {
JSClass CallableObjectClass = {
"CallableObject",
JSCLASS_HAS_RESERVED_SLOTS(2) |
JSCLASS_NEW_ENUMERATE,
@ -1064,8 +1067,8 @@ JSClass js_CallableObjectClass = {
NULL, NULL, NULL, NULL
};
JS_PUBLIC_API(JSBool)
JS_FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp)
JS_FRIEND_API(JSBool)
FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp)
{
AutoValueRooter tvr(cx);
if (!JSProxy::fix(cx, proxy, tvr.addr()))
@ -1081,7 +1084,7 @@ JS_FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp)
JSObject *proto = proxy->getProto();
JSObject *parent = proxy->getParent();
JSClass *clasp = proxy->isFunctionProxy() ? &js_CallableObjectClass : &js_ObjectClass;
JSClass *clasp = proxy->isFunctionProxy() ? &CallableObjectClass : &js_ObjectClass;
/* Make a blank object from the recipe fix provided to us. */
JSObject *newborn = NewObjectWithGivenProto(cx, clasp, proto, parent);
@ -1089,7 +1092,7 @@ JS_FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp)
return NULL;
AutoValueRooter tvr2(cx, newborn);
if (clasp == &js_CallableObjectClass) {
if (clasp == &CallableObjectClass) {
newborn->fslots[JSSLOT_CALLABLE_CALL] = proxy->fslots[JSSLOT_PROXY_CALL];
newborn->fslots[JSSLOT_CALLABLE_CONSTRUCT] = proxy->fslots[JSSLOT_PROXY_CONSTRUCT];
}
@ -1106,13 +1109,19 @@ JS_FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp)
return true;
}
JS_PUBLIC_API(JSBool)
JS_Becomes(JSContext *cx, JSObject *obj, JSObject *obj2)
{
#ifdef JS_THREADSAFE
JS_ASSERT_IF(obj->isNative(), obj->scope()->title.ownercx == cx);
JS_ASSERT_IF(obj2->isNative(), obj2->scope()->title.ownercx == cx);
#endif
obj->swap(obj2);
return true;
}
JS_FRIEND_API(JSObject *)
js_InitProxyClass(JSContext *cx, JSObject *obj)
{
JSObject *module = NewObject(cx, &js_ObjectClass, NULL, obj);
if (!module)
return NULL;
if (!JS_DefineProperty(cx, obj, "Proxy", OBJECT_TO_JSVAL(module),
JS_PropertyStub, JS_PropertyStub, 0)) {
return NULL;
}
if (!JS_DefineFunctions(cx, module, static_methods))
return NULL;
return obj;
}

Просмотреть файл

@ -45,6 +45,8 @@
#include "jsapi.h"
#include "jsobj.h"
namespace js {
/* Base class for all C++ proxy handlers. */
class JSProxyHandler {
public:
@ -68,6 +70,7 @@ class JSProxyHandler {
/* Spidermonkey extensions. */
virtual void finalize(JSContext *cx, JSObject *proxy);
virtual void trace(JSTracer *trc, JSObject *proxy);
virtual void *family() = 0;
};
@ -99,6 +102,7 @@ class JSNoopProxyHandler {
/* Spidermonkey extensions. */
virtual JS_FRIEND_API(void) finalize(JSContext *cx, JSObject *proxy);
virtual JS_FRIEND_API(void) trace(JSTracer *trc, JSObject *proxy);
virtual JS_FRIEND_API(void) *family();
static JSNoopProxyHandler singleton;
@ -143,20 +147,22 @@ const uint32 JSSLOT_PROXY_PRIVATE = JSSLOT_PRIVATE + 2;
const uint32 JSSLOT_PROXY_CALL = JSSLOT_PRIVATE + 1;
const uint32 JSSLOT_PROXY_CONSTRUCT = JSSLOT_PRIVATE + 2;
extern JS_FRIEND_API(JSClass) js_ObjectProxyClass;
extern JS_FRIEND_API(JSClass) js_FunctionProxyClass;
extern JSClass js_CallableObjectClass;
extern JS_FRIEND_API(JSClass) ObjectProxyClass;
extern JS_FRIEND_API(JSClass) FunctionProxyClass;
extern JSClass CallableObjectClass;
}
inline bool
JSObject::isObjectProxy() const
{
return getClass() == &js_ObjectProxyClass;
return getClass() == &js::ObjectProxyClass;
}
inline bool
JSObject::isFunctionProxy() const
{
return getClass() == &js_FunctionProxyClass;
return getClass() == &js::FunctionProxyClass;
}
inline bool
@ -169,7 +175,7 @@ inline jsval
JSObject::getProxyHandler() const
{
JS_ASSERT(isProxy());
jsval handler = fslots[JSSLOT_PROXY_HANDLER];
jsval handler = fslots[js::JSSLOT_PROXY_HANDLER];
JS_ASSERT(JSVAL_IS_OBJECT(handler) || JSVAL_IS_INT(handler));
return handler;
}
@ -178,30 +184,29 @@ inline jsval
JSObject::getProxyPrivate() const
{
JS_ASSERT(isObjectProxy());
return fslots[JSSLOT_PROXY_PRIVATE];
return fslots[js::JSSLOT_PROXY_PRIVATE];
}
inline void
JSObject::setProxyPrivate(jsval priv)
{
JS_ASSERT(isObjectProxy());
fslots[JSSLOT_PROXY_PRIVATE] = priv;
fslots[js::JSSLOT_PROXY_PRIVATE] = priv;
}
JS_PUBLIC_API(JSObject *)
JS_NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSString *className);
namespace js {
JS_PUBLIC_API(JSObject *)
JS_NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSObject *call, JSObject *construct);
JS_FRIEND_API(JSObject *)
NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSString *className);
JS_PUBLIC_API(JSBool)
JS_GetProxyObjectClass(JSContext *cx, JSObject *proxy, const char **namep);
JS_FRIEND_API(JSObject *)
NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSObject *call, JSObject *construct);
JS_PUBLIC_API(JSBool)
JS_FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp);
JS_FRIEND_API(JSBool)
GetProxyObjectClass(JSContext *cx, JSObject *proxy, const char **namep);
JS_PUBLIC_API(JSBool)
JS_Becomes(JSContext *cx, JSObject *obj, JSObject *obj2);
JS_FRIEND_API(JSBool)
FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp);
template <class T>
JSObject *
@ -211,17 +216,19 @@ JSNoopProxyHandler::wrap(JSContext *cx, JSObject *obj, JSObject *proto, JSObject
JSNoopProxyHandler *handler = new T(obj);
if (!handler)
return NULL;
JSObject *wrapper = JS_NewFunctionProxy(cx, PRIVATE_TO_JSVAL(handler), proto, parent, obj, NULL);
JSObject *wrapper = NewFunctionProxy(cx, PRIVATE_TO_JSVAL(handler), proto, parent, obj, NULL);
if (!wrapper)
delete handler;
return wrapper;
}
JSObject *wrapper = JS_NewObjectProxy(cx, PRIVATE_TO_JSVAL(&T::singleton), proto, parent, className);
JSObject *wrapper = NewObjectProxy(cx, PRIVATE_TO_JSVAL(&T::singleton), proto, parent, className);
if (wrapper)
wrapper->setProxyPrivate(OBJECT_TO_JSVAL(obj));
return wrapper;
}
}
JS_BEGIN_EXTERN_C
extern JS_FRIEND_API(JSObject *)

Просмотреть файл

@ -13946,6 +13946,8 @@ TraceRecorder::record_JSOP_BINDNAME()
JSAtom *atom = atoms[GET_INDEX(cx->regs->pc)];
jsid id = ATOM_TO_JSID(atom);
JSObject *obj2 = js_FindIdentifierBase(cx, fp->scopeChain, id);
if (!obj2)
RETURN_ERROR_A("error in js_FindIdentifierBase");
if (obj2 != globalObj && obj2->getClass() != &js_CallClass)
RETURN_STOP_A("BINDNAME on non-global, non-call object");
@ -14012,7 +14014,7 @@ TraceRecorder::record_JSOP_IN()
if (!localtm.recorder) {
if (prop)
obj2->dropProperty(localcx, prop);
return ARECORD_STOP;
return ARECORD_ABORTED;
}
if (!ok)