Function & Array prototype fixes. InstanceOf implementation.

This commit is contained in:
rogerl%netscape.com 2003-02-18 22:36:27 +00:00
Родитель 10603fb685
Коммит 50190e0cda
10 изменённых файлов: 151 добавлений и 60 удалений

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

@ -76,7 +76,7 @@ js2val setLength(JS2Metadata *meta, JS2Object *obj, uint32 length)
js2val Array_Constructor(JS2Metadata *meta, const js2val /*thisValue*/, js2val *argv, uint32 argc)
{
js2val thatValue = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass));
js2val thatValue = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass));
ArrayInstance *arrInst = checked_cast<ArrayInstance *>(JS2VAL_TO_OBJECT(thatValue));
if (argc > 0) {
if (argc == 1) {
@ -107,7 +107,9 @@ js2val Array_Constructor(JS2Metadata *meta, const js2val /*thisValue*/, js2val *
static js2val Array_toString(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/)
{
if (meta->objectType(thisValue) != meta->arrayClass)
if (!JS2VAL_IS_OBJECT(thisValue)
|| (JS2VAL_TO_OBJECT(thisValue)->kind != PrototypeInstanceKind)
|| ((checked_cast<PrototypeInstance *>(JS2VAL_TO_OBJECT(thisValue)))->type != meta->arrayClass))
meta->reportError(Exception::typeError, "Array.prototype.toString called on a non Array", meta->engine->errorPos());
ArrayInstance *arrInst = checked_cast<ArrayInstance *>(JS2VAL_TO_OBJECT(thisValue));
@ -137,7 +139,9 @@ static js2val Array_toString(JS2Metadata *meta, const js2val thisValue, js2val *
static js2val Array_toSource(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/)
{
if (meta->objectType(thisValue) != meta->arrayClass)
if (!JS2VAL_IS_OBJECT(thisValue)
|| (JS2VAL_TO_OBJECT(thisValue)->kind != PrototypeInstanceKind)
|| ((checked_cast<PrototypeInstance *>(JS2VAL_TO_OBJECT(thisValue)))->type != meta->arrayClass))
meta->reportError(Exception::typeError, "Array.prototype.toString called on a non Array", meta->engine->errorPos());
ArrayInstance *arrInst = checked_cast<ArrayInstance *>(JS2VAL_TO_OBJECT(thisValue));
@ -204,7 +208,7 @@ js2val Array_concat(JS2Metadata *meta, const js2val thisValue, js2val *argv, uin
{
js2val E = thisValue;
js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass));
js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass));
ArrayInstance *A = checked_cast<ArrayInstance *>(JS2VAL_TO_OBJECT(result));
uint32 n = 0;
uint32 i = 0;
@ -354,7 +358,7 @@ static js2val Array_slice(JS2Metadata *meta, const js2val thisValue, js2val *arg
ASSERT(JS2VAL_IS_OBJECT(thisValue));
JS2Object *thisObj = JS2VAL_TO_OBJECT(thisValue);
js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass));
js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass));
ArrayInstance *A = checked_cast<ArrayInstance *>(JS2VAL_TO_OBJECT(result));
uint32 length = getLength(meta, thisObj);
@ -580,7 +584,7 @@ static js2val Array_splice(JS2Metadata *meta, const js2val thisValue, js2val *ar
JS2Object *thisObj = JS2VAL_TO_OBJECT(thisValue);
uint32 length = getLength(meta, thisObj);
js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass));
js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass));
ArrayInstance *A = checked_cast<ArrayInstance *>(JS2VAL_TO_OBJECT(result));
int32 arg0 = meta->toInteger(argv[0]);
@ -803,6 +807,15 @@ void initArrayObject(JS2Metadata *meta)
publicNamespaceList.push_back(meta->publicNamespace);
meta->arrayClass->construct = Array_Constructor;
meta->arrayClass->prototype = new ArrayInstance(meta->objectClass->prototype, meta->arrayClass);
// Adding "prototype" & "length" as static members of the class - not dynamic properties; XXX
meta->env->addFrame(meta->arrayClass);
Variable *v = new Variable(meta->arrayClass, OBJECT_TO_JS2VAL(meta->arrayClass->prototype), true);
meta->defineLocalMember(meta->env, meta->engine->prototype_StringAtom, &publicNamespaceList, Attribute::NoOverride, false, ReadWriteAccess, v, 0);
v = new Variable(meta->numberClass, INT_TO_JS2VAL(1), true);
meta->defineLocalMember(meta->env, meta->engine->length_StringAtom, &publicNamespaceList, Attribute::NoOverride, false, ReadWriteAccess, v, 0);
meta->env->removeTopFrame();
PrototypeFunction *pf = &arrayProtos[0];
while (pf->name) {

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

@ -488,6 +488,7 @@ namespace MetaData {
{ eNew, "New", U16 }, // <argCount:u16>
{ eCall, "Call", U16 }, // <argCount:u16>
{ eTypeof, "Typeof", 0 },
{ eInstanceof, "Instanceof", 0 },
{ eIs, "Is", 0 },
{ ePopv, "Popv", 0 },
@ -633,6 +634,7 @@ namespace MetaData {
return 0;
case eIs: // pop expr, pop type, push boolean
case eInstanceof:
return 1;
case eCoerce: // pop value, push coerced value (type is arg)

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

@ -128,6 +128,7 @@ enum JS2Op {
eNew, // <argCount:u16>
eCall, // <argCount:u16>
eTypeof,
eInstanceof,
eIs,
ePopv,

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

@ -57,35 +57,35 @@ namespace MetaData {
js2val Function_Constructor(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc)
{
if (argc) {
const String &srcLoc = widenCString("Function constructor source");
const String *bodyStr = meta->toString(argv[argc - 1]);
String functionExpr(widenCString("("));
if (argc > 1) {
for (uint32 i = 0; i < (argc - 1); i++) {
functionExpr += *meta->toString(argv[i]);
if (i < (argc - 2))
functionExpr += ",";
}
}
functionExpr += widenCString("){") + *bodyStr + widenCString("}");
if (argc) {
const String &srcLoc = widenCString("Function constructor source");
const String *bodyStr = meta->toString(argv[argc - 1]);
String functionExpr(widenCString("("));
if (argc > 1) {
for (uint32 i = 0; i < (argc - 1); i++) {
functionExpr += *meta->toString(argv[i]);
if (i < (argc - 2))
functionExpr += ",";
}
}
functionExpr += widenCString("){") + *bodyStr + widenCString("}");
Arena a;
Pragma::Flags flags = Pragma::js1; // XXX get flags from meta/context ?
Parser parser(meta->world, a, flags, functionExpr, srcLoc);
Arena a;
Pragma::Flags flags = Pragma::js1; // XXX get flags from meta/context ?
Parser parser(meta->world, a, flags, functionExpr, srcLoc);
FunctionExprNode *fnExpr = parser.parseFunctionExpression(meta->engine->errorPos());
ASSERT(parser.lexer.peek(true).hasKind(Token::end));
ASSERT(fnExpr); // otherwise, an exception would have been thrown out of here
JS2Class *exprType;
meta->ValidateExpression(&meta->cxt, meta->env, fnExpr);
meta->SetupExprNode(meta->env, RunPhase, fnExpr, &exprType);
ASSERT(parser.lexer.peek(true).hasKind(Token::end));
ASSERT(fnExpr); // otherwise, an exception would have been thrown out of here
JS2Class *exprType;
meta->ValidateExpression(&meta->cxt, meta->env, fnExpr);
meta->SetupExprNode(meta->env, RunPhase, fnExpr, &exprType);
ASSERT(fnExpr);
return OBJECT_TO_JS2VAL(fnExpr->obj);
}
}
else { // construct an empty function wrapper
js2val thatValue = OBJECT_TO_JS2VAL(new FunctionInstance(meta->functionClass->prototype, meta->functionClass));
FunctionInstance *fnInst = checked_cast<FunctionInstance *>(JS2VAL_TO_OBJECT(thatValue));
fnInst->fWrap = new FunctionWrapper(true, new ParameterFrame(JS2VAL_INACCESSIBLE, true));
fnInst->fWrap = new FunctionWrapper(true, new ParameterFrame(JS2VAL_INACCESSIBLE, true));
return thatValue;
}
}

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

@ -1593,6 +1593,8 @@ namespace MetaData {
case ExprNode::logicalAndEquals:
case ExprNode::logicalXorEquals:
case ExprNode::logicalOrEquals:
case ExprNode::comma:
case ExprNode::Instanceof:
{
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
@ -1656,19 +1658,12 @@ namespace MetaData {
}
}
break;
case ExprNode::comma:
case ExprNode::functionLiteral:
{
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
ValidateExpression(cxt, env, b->op1);
ValidateExpression(cxt, env, b->op2);
FunctionExprNode *f = checked_cast<FunctionExprNode *>(p);
f->obj = validateStaticFunction(&f->function, JS2VAL_INACCESSIBLE, true, true, cxt, env);
}
break;
case ExprNode::functionLiteral:
{
FunctionExprNode *f = checked_cast<FunctionExprNode *>(p);
f->obj = validateStaticFunction(&f->function, JS2VAL_INACCESSIBLE, true, true, cxt, env);
}
break;
default:
NOT_REACHED("Not Yet Implemented");
} // switch (p->getKind())
@ -2273,9 +2268,19 @@ doUnary:
SetupExprNode(env, phase, b->op2, exprType);
}
break;
case ExprNode::Instanceof:
{
BinaryExprNode *b = checked_cast<BinaryExprNode *>(p);
Reference *rVal = SetupExprNode(env, phase, b->op1, exprType);
if (rVal) rVal->emitReadBytecode(bCon, p->pos);
rVal = SetupExprNode(env, phase, b->op2, exprType);
if (rVal) rVal->emitReadBytecode(bCon, p->pos);
bCon->emitOp(eInstanceof, p->pos);
}
break;
case ExprNode::functionLiteral:
{
FunctionExprNode *f = checked_cast<FunctionExprNode *>(p);
{
FunctionExprNode *f = checked_cast<FunctionExprNode *>(p);
CompilationData *oldData = startCompilationUnit(f->function.fWrap->bCon, bCon->mSource, bCon->mSourceLocation);
env->addFrame(f->function.fWrap->compileFrame);
SetupStmt(env, phase, f->function.body);
@ -2283,8 +2288,8 @@ doUnary:
bCon->emitOp(eReturnVoid, p->pos);
env->removeTopFrame();
restoreCompilationUnit(oldData);
}
break;
}
break;
default:
NOT_REACHED("Not Yet Implemented");
}
@ -3293,9 +3298,8 @@ XXX see EvalAttributeExpression, where identifiers are being handled for now...
//
// ToString(ToUint32(name)) == name
//
ASSERT(dynamicProperties);
const DynamicPropertyMap::value_type e(*name, newValue);
dynamicProperties->insert(e);
dynamicProperties.insert(e);
const char16 *numEnd;
float64 f = stringToDouble(name->data(), name->data() + name->length(), numEnd);
@ -4628,7 +4632,7 @@ deleteClassProperty:
ParameterFrame *plural = checked_cast<ParameterFrame *>(pluralFrame);
ASSERT((plural->positionalCount == 0) || (plural->positional != NULL));
js2val argumentsVal = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass));
js2val argumentsVal = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass));
ArrayInstance *arrInst = checked_cast<ArrayInstance *>(JS2VAL_TO_OBJECT(argumentsVal));
uint32 i;
for (i = 0; ((i < argCount) && (i < plural->positionalCount)); i++) {

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

@ -579,7 +579,7 @@ public:
JS2Object *parent; // If this instance was created by calling new on a prototype function,
// the value of the functions prototype property at the time of the call;
// none otherwise.
// none otherwise. (aka [[prototype]], aka __prototype__)
JS2Class *type; // XXX used to determine [[class]] value
DynamicPropertyMap dynamicProperties; // A set of this instance's dynamic properties
virtual void markChildren();
@ -639,12 +639,12 @@ public:
virtual ~FunctionInstance() { }
};
// Array instances are simple instances created by the Array class, they
// Array instances are PrototypeInstances created by the Array class, they
// maintain the value of the 'length' property when 'indexable' elements
// are added.
class ArrayInstance : public SimpleInstance {
class ArrayInstance : public PrototypeInstance {
public:
ArrayInstance(JS2Class *type) : SimpleInstance(type) { }
ArrayInstance(JS2Object *parent, JS2Class *type) : PrototypeInstance(parent, type) { }
virtual void writeProperty(JS2Metadata *meta, const String *name, js2val newValue);
virtual ~ArrayInstance() { }
@ -925,6 +925,7 @@ typedef FrameList::iterator FrameListIterator;
class Environment : public JS2Object {
public:
Environment(SystemFrame *systemFrame, Frame *nextToLast) : JS2Object(EnvironmentKind) { frameList.push_back(nextToLast); frameList.push_back(systemFrame); }
virtual ~Environment() { }
JS2Class *getEnclosingClass();
FrameListIterator getRegionalFrame();

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

@ -235,16 +235,86 @@
case eIs:
{
a = pop(); // catch variable type
b = pop(); // exception object
JS2Class *c = meta->objectType(b);
if (!JS2VAL_IS_OBJECT(a))
b = pop();
a = pop(); // doing a is b
if (!JS2VAL_IS_OBJECT(b))
meta->reportError(Exception::badValueError, "Type expected", errorPos());
JS2Object *obj = JS2VAL_TO_OBJECT(a);
JS2Object *obj = JS2VAL_TO_OBJECT(b);
if (obj->kind != ClassKind)
meta->reportError(Exception::badValueError, "Type expected", errorPos());
JS2Class *isClass = checked_cast<JS2Class *>(obj);
push(c == isClass);
push(BOOLEAN_TO_JS2VAL(meta->objectType(a) == isClass));
}
break;
case eInstanceof: // XXX prototype version
{
b = pop();
a = pop(); // doing a instanceof b
if (!JS2VAL_IS_OBJECT(b))
meta->reportError(Exception::typeError, "Object expected for instanceof", errorPos());
JS2Object *obj = JS2VAL_TO_OBJECT(b);
if ((obj->kind == PrototypeInstanceKind)
&& (checked_cast<PrototypeInstance *>(obj)->type == meta->functionClass)) {
// XXX this is [[hasInstance]] from ECMA3
if (!JS2VAL_IS_OBJECT(a))
push(JS2VAL_FALSE);
else {
if (JS2VAL_TO_OBJECT(a)->kind != PrototypeInstanceKind)
meta->reportError(Exception::typeError, "PrototypeInstance expected for instanceof", errorPos());
JS2Object *a_protoObj = checked_cast<PrototypeInstance *>(JS2VAL_TO_OBJECT(a))->parent;
js2val b_protoVal;
JS2Object *b_protoObj = NULL;
Multiname mn(prototype_StringAtom); // gc safe because the content is rooted elsewhere
LookupKind lookup(true, JS2VAL_NULL); // make it a lexical lookup since we want it to
// fail if 'prototype' hasn't been defined
// XXX (prototype should always exist for functions)
if (meta->readProperty(&a, &mn, &lookup, RunPhase, &b_protoVal)) {
if (!JS2VAL_IS_OBJECT(b_protoVal))
meta->reportError(Exception::typeError, "Non-object prototype value in instanceOf", errorPos());
b_protoObj = JS2VAL_TO_OBJECT(b_protoVal);
}
bool result = false;
while (a_protoObj) {
if (b_protoObj == a_protoObj) {
result = true;
break;
}
a_protoObj = checked_cast<PrototypeInstance *>(a_protoObj)->parent;
}
push(BOOLEAN_TO_JS2VAL(result));
}
}
else { // XXX also support a instanceof <<class>> since some of these are
// the ECMA3 builtins.
if (obj->kind == ClassKind) {
if (!JS2VAL_IS_OBJECT(a))
push(JS2VAL_FALSE);
else {
if (JS2VAL_TO_OBJECT(a)->kind != PrototypeInstanceKind)
meta->reportError(Exception::typeError, "PrototypeInstance expected for instanceof", errorPos());
JS2Object *a_protoObj = checked_cast<PrototypeInstance *>(JS2VAL_TO_OBJECT(a))->parent;
JS2Object *b_protoObj = checked_cast<JS2Class *>(obj)->prototype;
bool result = false;
while (a_protoObj) {
if (b_protoObj == a_protoObj) {
result = true;
break;
}
a_protoObj = checked_cast<PrototypeInstance *>(a_protoObj)->parent;
}
push(BOOLEAN_TO_JS2VAL(result));
}
}
else
meta->reportError(Exception::typeError, "Function or Class expected in instanceOf", errorPos());
}
}
break;

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

@ -135,12 +135,12 @@
{
uint16 argCount = BytecodeContainer::getShort(pc);
pc += sizeof(uint16);
ArrayInstance *aInst = new ArrayInstance(meta->arrayClass);
ArrayInstance *aInst = new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass);
baseVal = OBJECT_TO_JS2VAL(aInst);
for (uint16 i = 0; i < argCount; i++) {
b = pop();
const DynamicPropertyMap::value_type e(*numberToString((argCount - 1) - i), b);
aInst->dynamicProperties->insert(e);
aInst->dynamicProperties.insert(e);
}
setLength(meta, aInst, argCount);
push(baseVal);

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

@ -142,7 +142,7 @@ namespace MetaData {
REMatchState *match = REExecute(thisInst->mRegExp, str->begin(), index, toInt32(str->length()), meta->toBoolean(globalMultiline));
if (match) {
PrototypeInstance *A = new PrototypeInstance(meta->objectClass->prototype, meta->objectClass);
PrototypeInstance *A = new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass);
result = OBJECT_TO_JS2VAL(A);
js2val matchStr = meta->engine->allocString(str->substr((uint32)match->startIndex, (uint32)match->endIndex - match->startIndex));
Multiname mname(&meta->world.identifiers[*meta->toString((long)0)], meta->publicNamespace);

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

@ -170,7 +170,7 @@ static js2val String_match(JS2Metadata *meta, const js2val thisValue, js2val *ar
return RegExp_exec(meta, regexp, &S, 1);
}
else {
PrototypeInstance *A = new PrototypeInstance(meta->objectClass->prototype, meta->objectClass);
PrototypeInstance *A = new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass);
int32 index = 0;
int32 lastIndex = 0;
while (true) {
@ -405,7 +405,7 @@ static js2val String_split(JS2Metadata *meta, const js2val thisValue, js2val *ar
{
const String *S = meta->toString(thisValue);
js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass));
js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass));
ArrayInstance *A = checked_cast<ArrayInstance *>(JS2VAL_TO_OBJECT(result));
uint32 lim;