diff --git a/mozilla/js/js2/icode.h b/mozilla/js/js2/icode.h index 82ee6a044e8..667611261d3 100644 --- a/mozilla/js/js2/icode.h +++ b/mozilla/js/js2/icode.h @@ -9,7 +9,7 @@ BRANCH, /* target label */ BRANCH_FALSE, /* target label, condition */ BRANCH_TRUE, /* target label, condition */ - CALL, /* result, base, target, args */ + CALL, /* result, target, this, args */ CAST, /* dest, rvalue, toType */ COMPARE_EQ, /* dest, source1, source2 */ COMPARE_GE, /* dest, source1, source2 */ @@ -20,6 +20,7 @@ COMPARE_NE, /* dest, source1, source2 */ DEBUGGER, /* drop to the debugger */ DELETE_PROP, /* dest, object, prop name */ + DIRECT_CALL, /* result, target, args */ DIVIDE, /* dest, source1, source2 */ ELEM_XCR, /* dest, base, index, value */ GENERIC_BINARY_OP, /* dest, op, source1, source2 */ @@ -144,7 +145,7 @@ class Call : public Instruction_4 { public: - /* result, base, target, args */ + /* result, target, this, args */ Call (TypedRegister aOp1, TypedRegister aOp2, TypedRegister aOp3, RegisterList aOp4) : Instruction_4 (CALL, aOp1, aOp2, aOp3, aOp4) {}; @@ -268,6 +269,22 @@ } }; + class DirectCall : public Instruction_3 { + public: + /* result, target, args */ + DirectCall (TypedRegister aOp1, JSFunction * aOp2, RegisterList aOp3) : + Instruction_3 + (DIRECT_CALL, aOp1, aOp2, aOp3) {}; + virtual Formatter& print(Formatter& f) { + f << opcodeNames[DIRECT_CALL] << "\t" << mOp1 << ", " << "JSFunction" << ", " << mOp3; + return f; + } + virtual Formatter& printOperands(Formatter& f, const JSValues& registers) { + f << mOp1.first << ", " << ArgList(mOp3, registers); + return f; + } + }; + class Divide : public Arithmetic { public: /* dest, source1, source2 */ @@ -1045,6 +1062,7 @@ "COMPARE_NE ", "DEBUGGER ", "DELETE_PROP ", + "DIRECT_CALL ", "DIVIDE ", "ELEM_XCR ", "GENERIC_BINARY_OP", diff --git a/mozilla/js/js2/icodegenerator.cpp b/mozilla/js/js2/icodegenerator.cpp index b6c86df0588..2d288b6314e 100644 --- a/mozilla/js/js2/icodegenerator.cpp +++ b/mozilla/js/js2/icodegenerator.cpp @@ -466,6 +466,14 @@ TypedRegister ICodeGenerator::call(TypedRegister target, TypedRegister thisArg, return dest; } +TypedRegister ICodeGenerator::directCall(JSFunction *target, RegisterList *args) +{ + TypedRegister dest(getTempRegister(), &Any_Type); + DirectCall *instr = new DirectCall(dest, target, *args); + iCode->push_back(instr); + return dest; +} + TypedRegister ICodeGenerator::getMethod(TypedRegister thisArg, uint32 slotIndex) { TypedRegister dest(getTempRegister(), &Any_Type); @@ -1102,8 +1110,16 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p, ret = newClass(clazz); ret = call(getStatic(clazz, className), ret, &args); } - else - NOT_REACHED("new , where is not a new-able type (whatever that means)"); // XXX Runtime error. + else { + // + // like 'new Boolean()' - see if the type has a constructor + // + JSFunction *f = value.type->getConstructor(); + if (f) + ret = directCall(f, &args); + else + NOT_REACHED("new , where is not a new-able type (whatever that means)"); // XXX Runtime error. + } } else if (value.isFunction()) { diff --git a/mozilla/js/js2/icodegenerator.h b/mozilla/js/js2/icodegenerator.h index 12abffaf8df..6415dee318b 100644 --- a/mozilla/js/js2/icodegenerator.h +++ b/mozilla/js/js2/icodegenerator.h @@ -243,6 +243,7 @@ namespace ICG { TypedRegister op(ICodeOp op, TypedRegister source1, TypedRegister source2); TypedRegister binaryOp(ICodeOp op, TypedRegister source1, TypedRegister source2); TypedRegister call(TypedRegister base, TypedRegister target, RegisterList *args); + TypedRegister directCall(JSFunction *target, RegisterList *args); TypedRegister getMethod(TypedRegister thisArg, uint32 slotIndex); void move(TypedRegister destination, TypedRegister source); diff --git a/mozilla/js/js2/interpreter.cpp b/mozilla/js/js2/interpreter.cpp index 4a1629da962..1a045316cfe 100644 --- a/mozilla/js/js2/interpreter.cpp +++ b/mozilla/js/js2/interpreter.cpp @@ -34,6 +34,7 @@ #include "interpreter.h" #include "jsclasses.h" #include "world.h" +#include "parser.h" #include "jsmath.h" #include @@ -129,13 +130,26 @@ public: ~autosaver() { mRef = mOld; } }; -ICodeModule* Context::compile(const String &source) +ICodeModule* Context::compileFunction(const String &source) { Arena a; String filename = widenCString("Some source source"); Parser p(getWorld(), a, source, filename); - StmtNode *parsedStatements = p.parseProgram(); - return genCode(parsedStatements, filename); + ExprNode* e = p.parseExpression(false); + ICodeGenerator icg(&getWorld(), getGlobalObject()); + ASSERT(e->getKind() == ExprNode::functionLiteral); + FunctionExprNode* f = static_cast(e); + icg.allocateParameter(getWorld().identifiers["this"]); // always parameter #0 + VariableBinding* v = f->function.parameters; + while (v) { + if (v->name && (v->name->getKind() == ExprNode::identifier)) + icg.allocateParameter((static_cast(v->name))->name); + v = v->next; + } + icg.genStmt(f->function.body); + ICodeModule* result = icg.complete(); + result->setFileName(filename); + return result; } JSValue Context::readEvalFile(FILE* in, const String& fileName) @@ -516,8 +530,13 @@ void Context::initContext() // 'Object', 'Date', 'RegExp', 'Array' etc are all (constructor) properties of the global object + // Some of these overlap with the predefined types above. The way we handle this is to set a + // 'constructor' function for types. When new is encountered, the type is queried for + // a 'constructor' function to be invoked. JSObject::initObjectObject(mGlobal); + JSFunction::initFunctionObject(mGlobal); + JSBoolean::initBooleanObject(mGlobal); // the 'Math' object just has some useful properties JSMath::initMathObject(mGlobal); @@ -634,8 +653,18 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) case CALL: { Call* call = static_cast(instruction); - ASSERT((*registers)[op2(call).first].isFunction()); // XXX runtime error - JSFunction *target = (*registers)[op2(call).first].function; + JSValue v = (*registers)[op2(call).first]; + JSFunction *target = NULL; + if (v.isFunction()) + target = v.function; + else + if (v.isObject()) { + JSType *t = dynamic_cast(v.object); + if (t) + target = t->getInvokor(); + } + if (!target) + throw new JSException("Call to non callable object"); if (target->isNative()) { RegisterList ¶ms = op4(call); JSValues argv(params.size() + 1); @@ -661,6 +690,34 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) } } + case DIRECT_CALL: + { + DirectCall* call = static_cast(instruction); + JSFunction *target = op2(call); + if (target->isNative()) { + RegisterList ¶ms = op3(call); + JSValues argv(params.size() + 1); + JSValues::size_type i = 1; + for (RegisterList::const_iterator src = params.begin(), end = params.end(); + src != end; ++src, ++i) { + argv[i] = (*registers)[src->first]; + } + JSValue result = static_cast(target)->mCode(this, argv); + if (op1(call).first != NotARegister) + (*registers)[op1(call).first] = result; + break; + } + else { + mLinkage = new Linkage(mLinkage, ++mPC, + mActivation, mGlobal, op1(call)); + mActivation = new Activation(target->getICode(), mActivation, kNullValue, op3(call)); + registers = &mActivation->mRegisters; + mPC = mActivation->mICode->its_iCode->begin(); + endPC = mActivation->mICode->its_iCode->end(); + continue; + } + } + case RETURN_VOID: { Linkage *linkage = mLinkage; @@ -766,16 +823,14 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) JSValue& value = (*registers)[src1(gp).first]; if (value.isObject()) { if (value.isType()) { - // I don't think this is necessary anymore - any get property - // on a class name should have been turned into a GET_STATIC - // by the codegen. - ASSERT(false); // REVISIT: should signal error if slot doesn't exist. JSClass* thisClass = dynamic_cast(value.type); if (thisClass && thisClass->hasStatic(*src2(gp))) { const JSSlot& slot = thisClass->getStatic(*src2(gp)); (*registers)[dst(gp).first] = (*thisClass)[slot.mIndex]; } + else + (*registers)[dst(gp).first] = value.object->getProperty(*src2(gp)); } else { (*registers)[dst(gp).first] = value.object->getProperty(*src2(gp)); } @@ -789,16 +844,14 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) JSValue& value = (*registers)[dst(sp).first]; if (value.isObject()) { if (value.isType()) { - // I don't think this is necessary anymore - any set property - // on a class name should have been turned into a SET_STATIC - // by the codegen. - ASSERT(false); // REVISIT: should signal error if slot doesn't exist. JSClass* thisClass = dynamic_cast(value.object); if (thisClass && thisClass->hasStatic(*src1(sp))) { const JSSlot& slot = thisClass->getStatic(*src1(sp)); (*thisClass)[slot.mIndex] = (*registers)[src2(sp).first]; } + else + value.object->setProperty(*src1(sp), (*registers)[src2(sp).first]); } else { value.object->setProperty(*src1(sp), (*registers)[src2(sp).first]); } diff --git a/mozilla/js/js2/interpreter.h b/mozilla/js/js2/interpreter.h index 0acf8320867..11c0b50954d 100644 --- a/mozilla/js/js2/interpreter.h +++ b/mozilla/js/js2/interpreter.h @@ -77,7 +77,7 @@ namespace Interpreter { JSValue interpret(ICodeModule* iCode, const JSValues& args); void doCall(JSFunction *target, Instruction *pc); - ICodeModule* compile(const String &source); + ICodeModule* compileFunction(const String &source); ICodeModule* genCode(StmtNode *p, const String &fileName); JSValue readEvalFile(FILE* in, const String& fileName); diff --git a/mozilla/js/js2/jstypes.cpp b/mozilla/js/js2/jstypes.cpp index 8a9b99dd6c3..ea6121391c4 100644 --- a/mozilla/js/js2/jstypes.cpp +++ b/mozilla/js/js2/jstypes.cpp @@ -58,14 +58,11 @@ static JSValue object_toString(Context *, const JSValues& argv) return kUndefinedValue; } -static JSValue objectConstructor(Context *, const JSValues& argv) +static JSValue object_constructor(Context *cx, const JSValues& argv) { - ASSERT(argv.size() > 0); - JSValue theThis = argv[0]; + // argv[0] will be NULL - // the prototype and class have been established already - - return theThis; + return new JSObject(); } struct ObjectFunctionEntry { @@ -92,15 +89,13 @@ JSObject *JSObject::initJSObject() return result; } -// Install the 'Object' constructor into the scope, mostly irrelevant since making -// a new JSObject does all the work of setting the prototype and [[class]] values. void JSObject::initObjectObject(JSScope *g) { - JSNativeFunction *objCon = new JSNativeFunction(objectConstructor); - - objCon->setProperty(widenCString("prototype"), JSValue(ObjectPrototypeObject)); + // The ObjectPrototypeObject has already been constructed by static initialization. - g->setProperty(*ObjectString, JSValue(objCon)); +// JSNativeFunction *objCon = new JSNativeFunction(objectConstructor); +// objCon->setProperty(widenCString("prototype"), JSValue(ObjectPrototypeObject)); +// g->setProperty(*ObjectString, JSValue(objCon)); } @@ -116,17 +111,29 @@ static JSValue functionPrototypeFunction(Context *, const JSValues &) static JSValue function_constructor(Context *cx, const JSValues& argv) { - // build a function from the arguments into the this. - ASSERT(argv.size() > 0); - JSValue theThis = argv[0]; - ASSERT(theThis.isObject()); + // build a function from the arguments + // argv[0] will be NULL - if (argv.size() == 2) { - JSValue s = JSValue::valueToString(argv[1]); - theThis = new JSFunction(cx->compile((String)(*s.string))); + if (argv.size() >= 2) { + int32 parameterCount = argv.size() - 2; + JSString source("function ("); + for (int32 i = 0; i < parameterCount; i++) { + source.append((JSValue::valueToString(argv[i + 1]).string)); + if (i < (parameterCount - 1)) source.append(","); + } + source.append(") {"); + source.append(JSValue::valueToString(argv[argv.size() - 1]).string); + source.append("}"); + + JSFunction *f = new JSFunction(cx->compileFunction(source)); + f->setProperty(widenCString("length"), JSValue(parameterCount)); + JSObject *obj = new JSObject(); + f->setProperty(widenCString("prototype"), JSValue(obj)); + f->setProperty(widenCString("constructor"), JSValue(obj)); + return JSValue(f); } - return theThis; + return kUndefinedValue; } static JSValue function_toString(Context *, const JSValues &) @@ -145,7 +152,6 @@ static JSValue function_call(Context *, const JSValues &) } - JSString* JSFunction::FunctionString = new JSString("Function"); JSObject* JSFunction::FunctionPrototypeObject = NULL; // the 'original Function prototype object' @@ -166,19 +172,87 @@ void JSFunction::initFunctionObject(JSScope *g) for (int i = 0; i < sizeof(FunctionFunctions) / sizeof(FunctionFunctionEntry); i++) FunctionPrototypeObject->setProperty(widenCString(FunctionFunctions[i].name), JSValue(new JSNativeFunction(FunctionFunctions[i].fn) ) ); - // now the Function Constructor Object - JSNativeFunction *functionConstructorObject = new JSNativeFunction(function_constructor); - functionConstructorObject->setPrototype(FunctionPrototypeObject); - functionConstructorObject->setProperty(widenCString("length"), JSValue((int32)1)); - functionConstructorObject->setProperty(widenCString("prototype"), JSValue(FunctionPrototypeObject)); - - // This is interesting - had to use defineVariable here to specify a type because - // when left as Any_Type (via setProperty), the Function predefined type interacted - // badly with this value. (I think setProperty perhaps should have reset the entry - // in mTypes) (?) - g->defineVariable(*FunctionString, &Function_Type, JSValue(functionConstructorObject)); + ASSERT(g->getProperty(*FunctionString).isObject()); + JSObject *functionVariable = g->getProperty(*FunctionString).object; + // there is actually no connection between the 'prototype' property of the function object + // and the prototype set for each new function - the constructor has implicit access to + // the 'original Function prototype object' + functionVariable->setProperty(widenCString("prototype"), JSValue(FunctionPrototypeObject)); // should be DontEnum, DontDelete, ReadOnly } +/********** Boolean Object Stuff **************************/ + +JSString* JSBoolean::BooleanString = new JSString("Boolean"); + +JSValue boolean_constructor(Context *cx, const JSValues& argv) +{ + // argv[0] will be NULL + if (argv.size() > 1) + return JSValue(new JSBoolean(JSValue::valueToBoolean(argv[1]).boolean)); + else + return JSValue(new JSBoolean(false)); +} + +JSValue boolean_toString(Context *cx, const JSValues& argv) +{ + if (argv.size() > 0) { + JSValue theThis = argv[0]; + if (theThis.isObject()) { + JSBoolean *b = dynamic_cast(theThis.object); + if (b) + return JSValue(new JSString(b->getValue() ? "true" : "false")); + else + throw new JSException("TypeError : Boolean::toString called on non boolean object"); + } + else + throw new JSException("TypeError : Boolean::toString called on non object"); + } + return kUndefinedValue; +} + +JSValue boolean_valueOf(Context *cx, const JSValues& argv) +{ + return kUndefinedValue; +} + +JSObject *JSBoolean::BooleanPrototypeObject = NULL; + +struct BooleanFunctionEntry { + char *name; + JSNativeFunction::JSCode fn; +} BooleanFunctions[] = { + { "constructor", boolean_constructor }, + { "toString", boolean_toString }, + { "valueOf", boolean_valueOf }, +}; + +void JSBoolean::initBooleanObject(JSScope *g) +{ + BooleanPrototypeObject = new JSObject(); + BooleanPrototypeObject->setClass(new JSString(BooleanString)); + + for (int i = 0; i < sizeof(BooleanFunctions) / sizeof(BooleanFunctionEntry); i++) + BooleanPrototypeObject->setProperty(widenCString(BooleanFunctions[i].name), JSValue(new JSNativeFunction(BooleanFunctions[i].fn) ) ); + + ASSERT(g->getProperty(*BooleanString).isObject()); + JSObject *booleanVariable = g->getProperty(*BooleanString).object; + booleanVariable->setProperty(widenCString("prototype"), JSValue(BooleanPrototypeObject)); // should be DontEnum, DontDelete, ReadOnly +} + +/********** Date Object Stuff **************************/ + +JSValue date_constructor(Context *cx, const JSValues& argv) +{ + // return JSValue(new JSDate()); + return JSValue(new JSObject()); +} + +JSValue date_invokor(Context *cx, const JSValues& argv) +{ + return JSValue(new JSString("now")); +} + + /**************************************************************************************/ JSType Any_Type = JSType(widenCString("any"), NULL); @@ -186,15 +260,19 @@ JSType Integer_Type = JSType(widenCString("Integer"), &Any_Type); JSType Number_Type = JSType(widenCString("Number"), &Integer_Type); JSType Character_Type = JSType(widenCString("Character"), &Any_Type); JSType String_Type = JSType(widenCString("String"), &Character_Type); -JSType Function_Type = JSType(widenCString("Function"), &Any_Type); +JSType Function_Type = JSType(widenCString("Function"), &Any_Type, new JSNativeFunction(function_constructor), new JSNativeFunction(function_constructor)); JSType Array_Type = JSType(widenCString("Array"), &Any_Type); JSType Type_Type = JSType(widenCString("Type"), &Any_Type); -JSType Boolean_Type = JSType(widenCString("Boolean"), &Any_Type); +JSType Boolean_Type = JSType(widenCString("Boolean"), &Any_Type, new JSNativeFunction(boolean_constructor), new JSNativeFunction(boolean_constructor)); JSType Null_Type = JSType(widenCString("Null"), &Any_Type); JSType Void_Type = JSType(widenCString("void"), &Any_Type); JSType None_Type = JSType(widenCString("none"), &Any_Type); +JSType Object_Type = JSType(widenCString("Object"), NULL, new JSNativeFunction(object_constructor)); +JSType Date_Type = JSType(widenCString("Date"), NULL, new JSNativeFunction(date_constructor), new JSNativeFunction(date_invokor)); + + #ifdef IS_LITTLE_ENDIAN #define JSDOUBLE_HI32(x) (((uint32 *)&(x))[1]) #define JSDOUBLE_LO32(x) (((uint32 *)&(x))[0]) diff --git a/mozilla/js/js2/jstypes.h b/mozilla/js/js2/jstypes.h index 798b46fc01d..e415d5e1949 100644 --- a/mozilla/js/js2/jstypes.h +++ b/mozilla/js/js2/jstypes.h @@ -199,6 +199,7 @@ namespace JSTypes { extern const JSValue kNegativeInfinity; extern const JSValue kPositiveInfinity; + // JS2 predefined types: extern JSType Any_Type; extern JSType Integer_Type; extern JSType Number_Type; @@ -212,6 +213,10 @@ namespace JSTypes { extern JSType Void_Type; extern JSType None_Type; + // JS1X heritage classes as types: + extern JSType Object_Type; + extern JSType Date_Type; + typedef std::map, gc_map_allocator> JSProperties; /** @@ -554,8 +559,19 @@ namespace JSTypes { protected: String mName; JSType *mBaseType; + + // The constructor is an implementation of the [[Construct]] mechanism + JSFunction *mConstructor; + // The invokor is an implementation of the [[Call]] mechanism + JSFunction *mInvokor; public: - JSType(const String &name, JSType *baseType) : mName(name), mBaseType(baseType) + JSType(const String &name, JSType *baseType) : mName(name), mBaseType(baseType), mConstructor(NULL), mInvokor(NULL) + { + mType = &Type_Type; + } + + JSType(const String &name, JSType *baseType, JSFunction *constructor, JSFunction *invokor = NULL) + : mName(name), mBaseType(baseType), mConstructor(constructor), mInvokor(invokor) { mType = &Type_Type; } @@ -565,6 +581,22 @@ namespace JSTypes { const String& getName() const { return mName; } int32 distance(const JSType *other) const; + + JSFunction *getConstructor() const { return mConstructor; } + JSFunction *getInvokor() const { return mInvokor; } + }; + + class JSBoolean : public JSObject { + protected: + bool mValue; + static JSObject* BooleanPrototypeObject; + static JSString* BooleanString; + public: + JSBoolean(bool value) : mValue(value), JSObject(BooleanPrototypeObject) { setClass(BooleanString); } + + static void initBooleanObject(JSScope *g); + + bool getValue() { return mValue; } }; } /* namespace JSTypes */ diff --git a/mozilla/js/js2/tools/gencode.pl b/mozilla/js/js2/tools/gencode.pl index 464151705ba..882298ffbb6 100644 --- a/mozilla/js/js2/tools/gencode.pl +++ b/mozilla/js/js2/tools/gencode.pl @@ -288,9 +288,15 @@ $ops{"RETURN_VOID"} = $ops{"CALL"} = { super => "Instruction_4", - rem => "result, base, target, args", + rem => "result, target, this, args", params => [ ("TypedRegister", "TypedRegister", "TypedRegister", "RegisterList") ] }; +$ops{"DIRECT_CALL"} = + { + super => "Instruction_3", + rem => "result, target, args", + params => [ ("TypedRegister", "JSFunction *", "RegisterList") ] + }; $ops{"GET_METHOD"} = { super => "Instruction_3", @@ -535,22 +541,20 @@ sub get_print_body { for $type (@types) { if ($type eq "TypedRegister") { - push (@oplist, "mOp$op" ); - - # push (@oplist, "\"R\" << ((mOp$op.first == NotARegister) ? -1 : mOp$op.first)"); - } elsif ($type eq "Label*") { push (@oplist, "\"Offset \" << ((mOp$op) ? mOp$op->mOffset : NotAnOffset)") } elsif ($type =~ /String/) { push (@oplist, "\"'\" << *mOp$op << \"'\""); - } elsif ($type =~ /JSType\*/) { + } elsif ($type =~ /JSType *\*/) { push (@oplist, "\"'\" << mOp$op->getName() << \"'\""); + } elsif ($type =~ /JSFunction *\*/) { + push (@oplist, "\"JSFunction\""); } elsif ($type =~ /bool/) { push (@oplist, "\"'\" << ((mOp$op) ? \"true\" : \"false\") << \"'\""); } elsif ($type =~ /ICodeModule/) { push (@oplist, "\"ICodeModule\""); - } elsif ($type =~ /JSClass\*/) { + } elsif ($type =~ /JSClass *\*/) { push (@oplist, "mOp$op->getName()"); } else { push (@oplist, "mOp$op"); diff --git a/mozilla/js2/src/icode.h b/mozilla/js2/src/icode.h index 82ee6a044e8..667611261d3 100644 --- a/mozilla/js2/src/icode.h +++ b/mozilla/js2/src/icode.h @@ -9,7 +9,7 @@ BRANCH, /* target label */ BRANCH_FALSE, /* target label, condition */ BRANCH_TRUE, /* target label, condition */ - CALL, /* result, base, target, args */ + CALL, /* result, target, this, args */ CAST, /* dest, rvalue, toType */ COMPARE_EQ, /* dest, source1, source2 */ COMPARE_GE, /* dest, source1, source2 */ @@ -20,6 +20,7 @@ COMPARE_NE, /* dest, source1, source2 */ DEBUGGER, /* drop to the debugger */ DELETE_PROP, /* dest, object, prop name */ + DIRECT_CALL, /* result, target, args */ DIVIDE, /* dest, source1, source2 */ ELEM_XCR, /* dest, base, index, value */ GENERIC_BINARY_OP, /* dest, op, source1, source2 */ @@ -144,7 +145,7 @@ class Call : public Instruction_4 { public: - /* result, base, target, args */ + /* result, target, this, args */ Call (TypedRegister aOp1, TypedRegister aOp2, TypedRegister aOp3, RegisterList aOp4) : Instruction_4 (CALL, aOp1, aOp2, aOp3, aOp4) {}; @@ -268,6 +269,22 @@ } }; + class DirectCall : public Instruction_3 { + public: + /* result, target, args */ + DirectCall (TypedRegister aOp1, JSFunction * aOp2, RegisterList aOp3) : + Instruction_3 + (DIRECT_CALL, aOp1, aOp2, aOp3) {}; + virtual Formatter& print(Formatter& f) { + f << opcodeNames[DIRECT_CALL] << "\t" << mOp1 << ", " << "JSFunction" << ", " << mOp3; + return f; + } + virtual Formatter& printOperands(Formatter& f, const JSValues& registers) { + f << mOp1.first << ", " << ArgList(mOp3, registers); + return f; + } + }; + class Divide : public Arithmetic { public: /* dest, source1, source2 */ @@ -1045,6 +1062,7 @@ "COMPARE_NE ", "DEBUGGER ", "DELETE_PROP ", + "DIRECT_CALL ", "DIVIDE ", "ELEM_XCR ", "GENERIC_BINARY_OP", diff --git a/mozilla/js2/src/icodegenerator.cpp b/mozilla/js2/src/icodegenerator.cpp index b6c86df0588..2d288b6314e 100644 --- a/mozilla/js2/src/icodegenerator.cpp +++ b/mozilla/js2/src/icodegenerator.cpp @@ -466,6 +466,14 @@ TypedRegister ICodeGenerator::call(TypedRegister target, TypedRegister thisArg, return dest; } +TypedRegister ICodeGenerator::directCall(JSFunction *target, RegisterList *args) +{ + TypedRegister dest(getTempRegister(), &Any_Type); + DirectCall *instr = new DirectCall(dest, target, *args); + iCode->push_back(instr); + return dest; +} + TypedRegister ICodeGenerator::getMethod(TypedRegister thisArg, uint32 slotIndex) { TypedRegister dest(getTempRegister(), &Any_Type); @@ -1102,8 +1110,16 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p, ret = newClass(clazz); ret = call(getStatic(clazz, className), ret, &args); } - else - NOT_REACHED("new , where is not a new-able type (whatever that means)"); // XXX Runtime error. + else { + // + // like 'new Boolean()' - see if the type has a constructor + // + JSFunction *f = value.type->getConstructor(); + if (f) + ret = directCall(f, &args); + else + NOT_REACHED("new , where is not a new-able type (whatever that means)"); // XXX Runtime error. + } } else if (value.isFunction()) { diff --git a/mozilla/js2/src/icodegenerator.h b/mozilla/js2/src/icodegenerator.h index 12abffaf8df..6415dee318b 100644 --- a/mozilla/js2/src/icodegenerator.h +++ b/mozilla/js2/src/icodegenerator.h @@ -243,6 +243,7 @@ namespace ICG { TypedRegister op(ICodeOp op, TypedRegister source1, TypedRegister source2); TypedRegister binaryOp(ICodeOp op, TypedRegister source1, TypedRegister source2); TypedRegister call(TypedRegister base, TypedRegister target, RegisterList *args); + TypedRegister directCall(JSFunction *target, RegisterList *args); TypedRegister getMethod(TypedRegister thisArg, uint32 slotIndex); void move(TypedRegister destination, TypedRegister source); diff --git a/mozilla/js2/src/interpreter.cpp b/mozilla/js2/src/interpreter.cpp index 4a1629da962..1a045316cfe 100644 --- a/mozilla/js2/src/interpreter.cpp +++ b/mozilla/js2/src/interpreter.cpp @@ -34,6 +34,7 @@ #include "interpreter.h" #include "jsclasses.h" #include "world.h" +#include "parser.h" #include "jsmath.h" #include @@ -129,13 +130,26 @@ public: ~autosaver() { mRef = mOld; } }; -ICodeModule* Context::compile(const String &source) +ICodeModule* Context::compileFunction(const String &source) { Arena a; String filename = widenCString("Some source source"); Parser p(getWorld(), a, source, filename); - StmtNode *parsedStatements = p.parseProgram(); - return genCode(parsedStatements, filename); + ExprNode* e = p.parseExpression(false); + ICodeGenerator icg(&getWorld(), getGlobalObject()); + ASSERT(e->getKind() == ExprNode::functionLiteral); + FunctionExprNode* f = static_cast(e); + icg.allocateParameter(getWorld().identifiers["this"]); // always parameter #0 + VariableBinding* v = f->function.parameters; + while (v) { + if (v->name && (v->name->getKind() == ExprNode::identifier)) + icg.allocateParameter((static_cast(v->name))->name); + v = v->next; + } + icg.genStmt(f->function.body); + ICodeModule* result = icg.complete(); + result->setFileName(filename); + return result; } JSValue Context::readEvalFile(FILE* in, const String& fileName) @@ -516,8 +530,13 @@ void Context::initContext() // 'Object', 'Date', 'RegExp', 'Array' etc are all (constructor) properties of the global object + // Some of these overlap with the predefined types above. The way we handle this is to set a + // 'constructor' function for types. When new is encountered, the type is queried for + // a 'constructor' function to be invoked. JSObject::initObjectObject(mGlobal); + JSFunction::initFunctionObject(mGlobal); + JSBoolean::initBooleanObject(mGlobal); // the 'Math' object just has some useful properties JSMath::initMathObject(mGlobal); @@ -634,8 +653,18 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) case CALL: { Call* call = static_cast(instruction); - ASSERT((*registers)[op2(call).first].isFunction()); // XXX runtime error - JSFunction *target = (*registers)[op2(call).first].function; + JSValue v = (*registers)[op2(call).first]; + JSFunction *target = NULL; + if (v.isFunction()) + target = v.function; + else + if (v.isObject()) { + JSType *t = dynamic_cast(v.object); + if (t) + target = t->getInvokor(); + } + if (!target) + throw new JSException("Call to non callable object"); if (target->isNative()) { RegisterList ¶ms = op4(call); JSValues argv(params.size() + 1); @@ -661,6 +690,34 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) } } + case DIRECT_CALL: + { + DirectCall* call = static_cast(instruction); + JSFunction *target = op2(call); + if (target->isNative()) { + RegisterList ¶ms = op3(call); + JSValues argv(params.size() + 1); + JSValues::size_type i = 1; + for (RegisterList::const_iterator src = params.begin(), end = params.end(); + src != end; ++src, ++i) { + argv[i] = (*registers)[src->first]; + } + JSValue result = static_cast(target)->mCode(this, argv); + if (op1(call).first != NotARegister) + (*registers)[op1(call).first] = result; + break; + } + else { + mLinkage = new Linkage(mLinkage, ++mPC, + mActivation, mGlobal, op1(call)); + mActivation = new Activation(target->getICode(), mActivation, kNullValue, op3(call)); + registers = &mActivation->mRegisters; + mPC = mActivation->mICode->its_iCode->begin(); + endPC = mActivation->mICode->its_iCode->end(); + continue; + } + } + case RETURN_VOID: { Linkage *linkage = mLinkage; @@ -766,16 +823,14 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) JSValue& value = (*registers)[src1(gp).first]; if (value.isObject()) { if (value.isType()) { - // I don't think this is necessary anymore - any get property - // on a class name should have been turned into a GET_STATIC - // by the codegen. - ASSERT(false); // REVISIT: should signal error if slot doesn't exist. JSClass* thisClass = dynamic_cast(value.type); if (thisClass && thisClass->hasStatic(*src2(gp))) { const JSSlot& slot = thisClass->getStatic(*src2(gp)); (*registers)[dst(gp).first] = (*thisClass)[slot.mIndex]; } + else + (*registers)[dst(gp).first] = value.object->getProperty(*src2(gp)); } else { (*registers)[dst(gp).first] = value.object->getProperty(*src2(gp)); } @@ -789,16 +844,14 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) JSValue& value = (*registers)[dst(sp).first]; if (value.isObject()) { if (value.isType()) { - // I don't think this is necessary anymore - any set property - // on a class name should have been turned into a SET_STATIC - // by the codegen. - ASSERT(false); // REVISIT: should signal error if slot doesn't exist. JSClass* thisClass = dynamic_cast(value.object); if (thisClass && thisClass->hasStatic(*src1(sp))) { const JSSlot& slot = thisClass->getStatic(*src1(sp)); (*thisClass)[slot.mIndex] = (*registers)[src2(sp).first]; } + else + value.object->setProperty(*src1(sp), (*registers)[src2(sp).first]); } else { value.object->setProperty(*src1(sp), (*registers)[src2(sp).first]); } diff --git a/mozilla/js2/src/interpreter.h b/mozilla/js2/src/interpreter.h index 0acf8320867..11c0b50954d 100644 --- a/mozilla/js2/src/interpreter.h +++ b/mozilla/js2/src/interpreter.h @@ -77,7 +77,7 @@ namespace Interpreter { JSValue interpret(ICodeModule* iCode, const JSValues& args); void doCall(JSFunction *target, Instruction *pc); - ICodeModule* compile(const String &source); + ICodeModule* compileFunction(const String &source); ICodeModule* genCode(StmtNode *p, const String &fileName); JSValue readEvalFile(FILE* in, const String& fileName); diff --git a/mozilla/js2/src/jstypes.cpp b/mozilla/js2/src/jstypes.cpp index 8a9b99dd6c3..ea6121391c4 100644 --- a/mozilla/js2/src/jstypes.cpp +++ b/mozilla/js2/src/jstypes.cpp @@ -58,14 +58,11 @@ static JSValue object_toString(Context *, const JSValues& argv) return kUndefinedValue; } -static JSValue objectConstructor(Context *, const JSValues& argv) +static JSValue object_constructor(Context *cx, const JSValues& argv) { - ASSERT(argv.size() > 0); - JSValue theThis = argv[0]; + // argv[0] will be NULL - // the prototype and class have been established already - - return theThis; + return new JSObject(); } struct ObjectFunctionEntry { @@ -92,15 +89,13 @@ JSObject *JSObject::initJSObject() return result; } -// Install the 'Object' constructor into the scope, mostly irrelevant since making -// a new JSObject does all the work of setting the prototype and [[class]] values. void JSObject::initObjectObject(JSScope *g) { - JSNativeFunction *objCon = new JSNativeFunction(objectConstructor); - - objCon->setProperty(widenCString("prototype"), JSValue(ObjectPrototypeObject)); + // The ObjectPrototypeObject has already been constructed by static initialization. - g->setProperty(*ObjectString, JSValue(objCon)); +// JSNativeFunction *objCon = new JSNativeFunction(objectConstructor); +// objCon->setProperty(widenCString("prototype"), JSValue(ObjectPrototypeObject)); +// g->setProperty(*ObjectString, JSValue(objCon)); } @@ -116,17 +111,29 @@ static JSValue functionPrototypeFunction(Context *, const JSValues &) static JSValue function_constructor(Context *cx, const JSValues& argv) { - // build a function from the arguments into the this. - ASSERT(argv.size() > 0); - JSValue theThis = argv[0]; - ASSERT(theThis.isObject()); + // build a function from the arguments + // argv[0] will be NULL - if (argv.size() == 2) { - JSValue s = JSValue::valueToString(argv[1]); - theThis = new JSFunction(cx->compile((String)(*s.string))); + if (argv.size() >= 2) { + int32 parameterCount = argv.size() - 2; + JSString source("function ("); + for (int32 i = 0; i < parameterCount; i++) { + source.append((JSValue::valueToString(argv[i + 1]).string)); + if (i < (parameterCount - 1)) source.append(","); + } + source.append(") {"); + source.append(JSValue::valueToString(argv[argv.size() - 1]).string); + source.append("}"); + + JSFunction *f = new JSFunction(cx->compileFunction(source)); + f->setProperty(widenCString("length"), JSValue(parameterCount)); + JSObject *obj = new JSObject(); + f->setProperty(widenCString("prototype"), JSValue(obj)); + f->setProperty(widenCString("constructor"), JSValue(obj)); + return JSValue(f); } - return theThis; + return kUndefinedValue; } static JSValue function_toString(Context *, const JSValues &) @@ -145,7 +152,6 @@ static JSValue function_call(Context *, const JSValues &) } - JSString* JSFunction::FunctionString = new JSString("Function"); JSObject* JSFunction::FunctionPrototypeObject = NULL; // the 'original Function prototype object' @@ -166,19 +172,87 @@ void JSFunction::initFunctionObject(JSScope *g) for (int i = 0; i < sizeof(FunctionFunctions) / sizeof(FunctionFunctionEntry); i++) FunctionPrototypeObject->setProperty(widenCString(FunctionFunctions[i].name), JSValue(new JSNativeFunction(FunctionFunctions[i].fn) ) ); - // now the Function Constructor Object - JSNativeFunction *functionConstructorObject = new JSNativeFunction(function_constructor); - functionConstructorObject->setPrototype(FunctionPrototypeObject); - functionConstructorObject->setProperty(widenCString("length"), JSValue((int32)1)); - functionConstructorObject->setProperty(widenCString("prototype"), JSValue(FunctionPrototypeObject)); - - // This is interesting - had to use defineVariable here to specify a type because - // when left as Any_Type (via setProperty), the Function predefined type interacted - // badly with this value. (I think setProperty perhaps should have reset the entry - // in mTypes) (?) - g->defineVariable(*FunctionString, &Function_Type, JSValue(functionConstructorObject)); + ASSERT(g->getProperty(*FunctionString).isObject()); + JSObject *functionVariable = g->getProperty(*FunctionString).object; + // there is actually no connection between the 'prototype' property of the function object + // and the prototype set for each new function - the constructor has implicit access to + // the 'original Function prototype object' + functionVariable->setProperty(widenCString("prototype"), JSValue(FunctionPrototypeObject)); // should be DontEnum, DontDelete, ReadOnly } +/********** Boolean Object Stuff **************************/ + +JSString* JSBoolean::BooleanString = new JSString("Boolean"); + +JSValue boolean_constructor(Context *cx, const JSValues& argv) +{ + // argv[0] will be NULL + if (argv.size() > 1) + return JSValue(new JSBoolean(JSValue::valueToBoolean(argv[1]).boolean)); + else + return JSValue(new JSBoolean(false)); +} + +JSValue boolean_toString(Context *cx, const JSValues& argv) +{ + if (argv.size() > 0) { + JSValue theThis = argv[0]; + if (theThis.isObject()) { + JSBoolean *b = dynamic_cast(theThis.object); + if (b) + return JSValue(new JSString(b->getValue() ? "true" : "false")); + else + throw new JSException("TypeError : Boolean::toString called on non boolean object"); + } + else + throw new JSException("TypeError : Boolean::toString called on non object"); + } + return kUndefinedValue; +} + +JSValue boolean_valueOf(Context *cx, const JSValues& argv) +{ + return kUndefinedValue; +} + +JSObject *JSBoolean::BooleanPrototypeObject = NULL; + +struct BooleanFunctionEntry { + char *name; + JSNativeFunction::JSCode fn; +} BooleanFunctions[] = { + { "constructor", boolean_constructor }, + { "toString", boolean_toString }, + { "valueOf", boolean_valueOf }, +}; + +void JSBoolean::initBooleanObject(JSScope *g) +{ + BooleanPrototypeObject = new JSObject(); + BooleanPrototypeObject->setClass(new JSString(BooleanString)); + + for (int i = 0; i < sizeof(BooleanFunctions) / sizeof(BooleanFunctionEntry); i++) + BooleanPrototypeObject->setProperty(widenCString(BooleanFunctions[i].name), JSValue(new JSNativeFunction(BooleanFunctions[i].fn) ) ); + + ASSERT(g->getProperty(*BooleanString).isObject()); + JSObject *booleanVariable = g->getProperty(*BooleanString).object; + booleanVariable->setProperty(widenCString("prototype"), JSValue(BooleanPrototypeObject)); // should be DontEnum, DontDelete, ReadOnly +} + +/********** Date Object Stuff **************************/ + +JSValue date_constructor(Context *cx, const JSValues& argv) +{ + // return JSValue(new JSDate()); + return JSValue(new JSObject()); +} + +JSValue date_invokor(Context *cx, const JSValues& argv) +{ + return JSValue(new JSString("now")); +} + + /**************************************************************************************/ JSType Any_Type = JSType(widenCString("any"), NULL); @@ -186,15 +260,19 @@ JSType Integer_Type = JSType(widenCString("Integer"), &Any_Type); JSType Number_Type = JSType(widenCString("Number"), &Integer_Type); JSType Character_Type = JSType(widenCString("Character"), &Any_Type); JSType String_Type = JSType(widenCString("String"), &Character_Type); -JSType Function_Type = JSType(widenCString("Function"), &Any_Type); +JSType Function_Type = JSType(widenCString("Function"), &Any_Type, new JSNativeFunction(function_constructor), new JSNativeFunction(function_constructor)); JSType Array_Type = JSType(widenCString("Array"), &Any_Type); JSType Type_Type = JSType(widenCString("Type"), &Any_Type); -JSType Boolean_Type = JSType(widenCString("Boolean"), &Any_Type); +JSType Boolean_Type = JSType(widenCString("Boolean"), &Any_Type, new JSNativeFunction(boolean_constructor), new JSNativeFunction(boolean_constructor)); JSType Null_Type = JSType(widenCString("Null"), &Any_Type); JSType Void_Type = JSType(widenCString("void"), &Any_Type); JSType None_Type = JSType(widenCString("none"), &Any_Type); +JSType Object_Type = JSType(widenCString("Object"), NULL, new JSNativeFunction(object_constructor)); +JSType Date_Type = JSType(widenCString("Date"), NULL, new JSNativeFunction(date_constructor), new JSNativeFunction(date_invokor)); + + #ifdef IS_LITTLE_ENDIAN #define JSDOUBLE_HI32(x) (((uint32 *)&(x))[1]) #define JSDOUBLE_LO32(x) (((uint32 *)&(x))[0]) diff --git a/mozilla/js2/src/jstypes.h b/mozilla/js2/src/jstypes.h index 798b46fc01d..e415d5e1949 100644 --- a/mozilla/js2/src/jstypes.h +++ b/mozilla/js2/src/jstypes.h @@ -199,6 +199,7 @@ namespace JSTypes { extern const JSValue kNegativeInfinity; extern const JSValue kPositiveInfinity; + // JS2 predefined types: extern JSType Any_Type; extern JSType Integer_Type; extern JSType Number_Type; @@ -212,6 +213,10 @@ namespace JSTypes { extern JSType Void_Type; extern JSType None_Type; + // JS1X heritage classes as types: + extern JSType Object_Type; + extern JSType Date_Type; + typedef std::map, gc_map_allocator> JSProperties; /** @@ -554,8 +559,19 @@ namespace JSTypes { protected: String mName; JSType *mBaseType; + + // The constructor is an implementation of the [[Construct]] mechanism + JSFunction *mConstructor; + // The invokor is an implementation of the [[Call]] mechanism + JSFunction *mInvokor; public: - JSType(const String &name, JSType *baseType) : mName(name), mBaseType(baseType) + JSType(const String &name, JSType *baseType) : mName(name), mBaseType(baseType), mConstructor(NULL), mInvokor(NULL) + { + mType = &Type_Type; + } + + JSType(const String &name, JSType *baseType, JSFunction *constructor, JSFunction *invokor = NULL) + : mName(name), mBaseType(baseType), mConstructor(constructor), mInvokor(invokor) { mType = &Type_Type; } @@ -565,6 +581,22 @@ namespace JSTypes { const String& getName() const { return mName; } int32 distance(const JSType *other) const; + + JSFunction *getConstructor() const { return mConstructor; } + JSFunction *getInvokor() const { return mInvokor; } + }; + + class JSBoolean : public JSObject { + protected: + bool mValue; + static JSObject* BooleanPrototypeObject; + static JSString* BooleanString; + public: + JSBoolean(bool value) : mValue(value), JSObject(BooleanPrototypeObject) { setClass(BooleanString); } + + static void initBooleanObject(JSScope *g); + + bool getValue() { return mValue; } }; } /* namespace JSTypes */ diff --git a/mozilla/js2/tools/gencode.pl b/mozilla/js2/tools/gencode.pl index 464151705ba..882298ffbb6 100644 --- a/mozilla/js2/tools/gencode.pl +++ b/mozilla/js2/tools/gencode.pl @@ -288,9 +288,15 @@ $ops{"RETURN_VOID"} = $ops{"CALL"} = { super => "Instruction_4", - rem => "result, base, target, args", + rem => "result, target, this, args", params => [ ("TypedRegister", "TypedRegister", "TypedRegister", "RegisterList") ] }; +$ops{"DIRECT_CALL"} = + { + super => "Instruction_3", + rem => "result, target, args", + params => [ ("TypedRegister", "JSFunction *", "RegisterList") ] + }; $ops{"GET_METHOD"} = { super => "Instruction_3", @@ -535,22 +541,20 @@ sub get_print_body { for $type (@types) { if ($type eq "TypedRegister") { - push (@oplist, "mOp$op" ); - - # push (@oplist, "\"R\" << ((mOp$op.first == NotARegister) ? -1 : mOp$op.first)"); - } elsif ($type eq "Label*") { push (@oplist, "\"Offset \" << ((mOp$op) ? mOp$op->mOffset : NotAnOffset)") } elsif ($type =~ /String/) { push (@oplist, "\"'\" << *mOp$op << \"'\""); - } elsif ($type =~ /JSType\*/) { + } elsif ($type =~ /JSType *\*/) { push (@oplist, "\"'\" << mOp$op->getName() << \"'\""); + } elsif ($type =~ /JSFunction *\*/) { + push (@oplist, "\"JSFunction\""); } elsif ($type =~ /bool/) { push (@oplist, "\"'\" << ((mOp$op) ? \"true\" : \"false\") << \"'\""); } elsif ($type =~ /ICodeModule/) { push (@oplist, "\"ICodeModule\""); - } elsif ($type =~ /JSClass\*/) { + } elsif ($type =~ /JSClass *\*/) { push (@oplist, "mOp$op->getName()"); } else { push (@oplist, "mOp$op");