/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape 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/NPL/ * * 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 JavaScript 2 Prototype. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU Public License (the "GPL"), in which case the * provisions of the GPL are applicable instead of those above. * If you wish to allow use of your version of this file only * under the terms of the GPL and not to allow others to use your * version of this file under the NPL, indicate your decision by * deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete * the provisions above, a recipient may use your version of this * file under either the NPL or the GPL. */ #ifdef _WIN32 #include "msvc_pragma.h" #endif #include #include #include #include #include #include "world.h" #include "utilities.h" #include "js2value.h" #include "jslong.h" #include "numerics.h" #include "reader.h" #include "parser.h" #include "regexp.h" #include "js2engine.h" #include "bytecodecontainer.h" #include "js2metadata.h" namespace JavaScript { namespace MetaData { js2val JS2Metadata::readEvalString(const String &str, const String& fileName) { js2val result = JS2VAL_VOID; Arena a; Pragma::Flags flags = Pragma::js1; Parser p(world, a, flags, str, fileName); CompilationData *oldData = NULL; try { StmtNode *parsedStatements = p.parseProgram(); ASSERT(p.lexer.peek(true).hasKind(Token::end)); if (showTrees) { PrettyPrinter f(stdOut, 80); { PrettyPrinter::Block b(f, 2); f << "Program ="; f.linearBreak(1); StmtNode::printStatements(f, parsedStatements); } f.end(); stdOut << '\n'; } if (parsedStatements) { oldData = startCompilationUnit(NULL, str, fileName); ValidateStmtList(parsedStatements); result = ExecuteStmtList(RunPhase, parsedStatements); } } catch (Exception &x) { if (oldData) restoreCompilationUnit(oldData); throw x; } if (oldData) restoreCompilationUnit(oldData); return result; } js2val JS2Metadata::readEvalFile(const String& fileName) { String buffer; int ch; js2val result = JS2VAL_VOID; std::string str(fileName.length(), char()); std::transform(fileName.begin(), fileName.end(), str.begin(), narrow); FILE* f = fopen(str.c_str(), "r"); if (f) { while ((ch = getc(f)) != EOF) buffer += static_cast(ch); fclose(f); result = readEvalString(buffer, fileName); } return result; } js2val JS2Metadata::readEvalFile(const char *fileName) { String buffer; int ch; js2val result = JS2VAL_VOID; FILE* f = fopen(fileName, "r"); if (f) { while ((ch = getc(f)) != EOF) buffer += static_cast(ch); fclose(f); result = readEvalString(buffer, widenCString(fileName)); } return result; } /* * Evaluate the linked list of statement nodes beginning at 'p' * (generate bytecode and then execute that bytecode */ js2val JS2Metadata::ExecuteStmtList(Phase phase, StmtNode *p) { Arena *oldArena = referenceArena; referenceArena = new Arena; size_t lastPos; try { lastPos = p->pos; while (p) { SetupStmt(env, phase, p); lastPos = p->pos; p = p->next; } } catch (Exception &x) { referenceArena->clear(); delete referenceArena; referenceArena = oldArena; throw x; } referenceArena->clear(); delete referenceArena; referenceArena = oldArena; bCon->emitOp(eReturnVoid, lastPos); uint8 *savePC = engine->pc; engine->pc = NULL; js2val retval = engine->interpret(phase, bCon, env); engine->pc = savePC; return retval; } /* * Evaluate an expression 'p' AND execute the associated bytecode */ js2val JS2Metadata::EvalExpression(Environment *env, Phase phase, ExprNode *p) { js2val retval; uint8 *savePC = NULL; JS2Class *exprType; Arena *oldArena = referenceArena; referenceArena = new Arena; CompilationData *oldData = startCompilationUnit(NULL, bCon->mSource, bCon->mSourceLocation); try { Reference *r = SetupExprNode(env, phase, p, &exprType); if (r) r->emitReadBytecode(bCon, p->pos); bCon->emitOp(eReturn, p->pos); savePC = engine->pc; engine->pc = NULL; retval = engine->interpret(phase, bCon, env); } catch (Exception &x) { referenceArena->clear(); delete referenceArena; referenceArena = oldArena; engine->pc = savePC; restoreCompilationUnit(oldData); throw x; } referenceArena->clear(); delete referenceArena; referenceArena = oldArena; engine->pc = savePC; restoreCompilationUnit(oldData); return retval; } // Execute an expression and return the result, which must be a type JS2Class *JS2Metadata::EvalTypeExpression(Environment *env, Phase phase, ExprNode *p) { js2val retval = EvalExpression(env, phase, p); if (JS2VAL_IS_PRIMITIVE(retval)) reportError(Exception::badValueError, "Type expected", p->pos); JS2Object *obj = JS2VAL_TO_OBJECT(retval); if (obj->kind != ClassKind) reportError(Exception::badValueError, "Type expected", p->pos); return checked_cast(obj); } // Invoke the named function on the thisValue object (it is an object) // Returns false if no callable function exists. Otherwise return the // function result value. bool JS2Metadata::invokeFunctionOnObject(js2val thisValue, const String *fnName, js2val &result) { js2val fnVal; JS2Class *limit = objectType(thisValue); if (limit->readPublic(this, &thisValue, limit, fnName, RunPhase, &fnVal)) { if (JS2VAL_IS_OBJECT(fnVal)) { JS2Object *fnObj = JS2VAL_TO_OBJECT(fnVal); if ((fnObj->kind == SimpleInstanceKind) && ((checked_cast(fnObj))->type == functionClass)) { result = invokeFunction(fnObj, thisValue, NULL, 0, NULL); return true; } } } return false; } // Invoke the named function, no args, by looking it up // the current scope. (Do this by emitting and executing the // appropriate bytecode sequence) js2val JS2Metadata::invokeFunction(const char *fname) { js2val retval; uint8 *savePC = NULL; CompilationData *oldData = startCompilationUnit(NULL, bCon->mSource, bCon->mSourceLocation); try { LexicalReference rVal(&world.identifiers[widenCString(fname)], false); rVal.emitReadForInvokeBytecode(bCon, 0); bCon->emitOp(eCall, 0, -(0 + 2) + 1); // pop argCount args, the base & function, and push a result bCon->addShort(0); bCon->emitOp(eReturn, 0); savePC = engine->pc; engine->pc = NULL; retval = engine->interpret(RunPhase, bCon, env); } catch (Exception &x) { engine->pc = savePC; restoreCompilationUnit(oldData); throw x; } engine->pc = savePC; restoreCompilationUnit(oldData); return retval; } // Invoke the constructor function for a class void JS2Metadata::invokeInit(JS2Class *c, js2val thisValue, js2val *argv, uint32 argc) { FunctionInstance *init = NULL; if (c) init = c->init; if (init) { ParameterFrame *runtimeFrame; DEFINE_ROOTKEEPER(rk, runtimeFrame); runtimeFrame = new ParameterFrame(init->fWrap->compileFrame); if (!init->fWrap->compileFrame->callsSuperConstructor) { invokeInit(c->super, thisValue, NULL, 0); runtimeFrame->superConstructorCalled = true; } invokeFunction(init, thisValue, argv, argc, runtimeFrame); if (!runtimeFrame->superConstructorCalled) reportError(Exception::uninitializedError, "The superconstuctor must be called before returning normally from a constructor", engine->errorPos()); } else if (argc) reportError(Exception::argumentsError, "The default constructor does not take any arguments", engine->errorPos()); } js2val JS2Metadata::invokeFunction(JS2Object *fnObj, js2val thisValue, js2val *argv, uint32 argc, ParameterFrame *runtimeFrame) { js2val result = JS2VAL_UNDEFINED; FunctionWrapper *fWrap = NULL; if ((fnObj->kind == SimpleInstanceKind) && ((checked_cast(fnObj))->type == functionClass)) { FunctionInstance *fInst = checked_cast(fnObj); fWrap = fInst->fWrap; if (fInst->isMethodClosure) { // XXX ignoring fInst->thisObject; } if (fWrap->code) { result = (fWrap->code)(this, thisValue, argv, argc); } else { uint8 *savePC = NULL; BytecodeContainer *bCon = fWrap->bCon; CompilationData *oldData = startCompilationUnit(bCon, bCon->mSource, bCon->mSourceLocation); DEFINE_ROOTKEEPER(rk, runtimeFrame); if (runtimeFrame == NULL) runtimeFrame = new ParameterFrame(fWrap->compileFrame); runtimeFrame->instantiate(fWrap->env); runtimeFrame->thisObject = thisValue; runtimeFrame->assignArguments(this, fnObj, argv, argc, argc); Frame *oldTopFrame = fWrap->env->getTopFrame(); if (fInst->isMethodClosure) fWrap->env->addFrame(objectType(thisValue)); fWrap->env->addFrame(runtimeFrame); ParameterFrame *oldPFrame = engine->parameterFrame; try { savePC = engine->pc; engine->pc = NULL; engine->parameterFrame = runtimeFrame; result = engine->interpret(RunPhase, bCon, fWrap->env); } catch (Exception &x) { engine->pc = savePC; restoreCompilationUnit(oldData); fWrap->env->setTopFrame(oldTopFrame); engine->parameterFrame = oldPFrame; throw x; } engine->pc = savePC; restoreCompilationUnit(oldData); fWrap->env->setTopFrame(oldTopFrame); engine->parameterFrame = oldPFrame; } } return result; } // Save off info about the current compilation and begin a // new one - using the given parser. CompilationData *JS2Metadata::startCompilationUnit(BytecodeContainer *newBCon, const String &source, const String &sourceLocation) { CompilationData *result = new CompilationData(); result->compilation_bCon = bCon; result->execution_bCon = engine->bCon; bConList.push_back(bCon); if (newBCon) bCon = newBCon; else bCon = new BytecodeContainer(); bCon->mSource = source; bCon->mSourceLocation = sourceLocation; engine->bCon = bCon; return result; } // Restore the compilation data, and then delete the cached copy. void JS2Metadata::restoreCompilationUnit(CompilationData *oldData) { BytecodeContainer *xbCon = bConList.back(); ASSERT(oldData->compilation_bCon == xbCon); bConList.pop_back(); bCon = oldData->compilation_bCon; engine->bCon = oldData->execution_bCon; delete oldData; } // x is not a String const String *JS2Metadata::convertValueToString(js2val x) { if (JS2VAL_IS_UNDEFINED(x)) return engine->undefined_StringAtom; if (JS2VAL_IS_NULL(x)) return engine->null_StringAtom; if (JS2VAL_IS_BOOLEAN(x)) return (JS2VAL_TO_BOOLEAN(x)) ? engine->true_StringAtom : engine->false_StringAtom; if (JS2VAL_IS_INT(x)) return engine->numberToString(JS2VAL_TO_INT(x)); if (JS2VAL_IS_LONG(x)) { float64 d; JSLL_L2D(d, *JS2VAL_TO_LONG(x)); return engine->numberToString(&d); } if (JS2VAL_IS_ULONG(x)) { float64 d; JSLL_UL2D(d, *JS2VAL_TO_ULONG(x)); return engine->numberToString(&d); } if (JS2VAL_IS_FLOAT(x)) { float64 d = *JS2VAL_TO_FLOAT(x); return engine->numberToString(&d); } if (JS2VAL_IS_DOUBLE(x)) return engine->numberToString(JS2VAL_TO_DOUBLE(x)); return toString(toPrimitive(x, StringHint)); } // x is not a primitive (it is an object and not null) js2val JS2Metadata::convertValueToPrimitive(js2val x, Hint hint) { // return [[DefaultValue]] --> get property 'toString' and invoke it, // if not available or result is not primitive then try property 'valueOf' // if that's not available or returns a non primitive, throw a TypeError JS2Object *obj = JS2VAL_TO_OBJECT(x); if (obj->kind == ClassKind) // therefore, not an E3 object, so just return return engine->typeofString(x); // the 'typeof' string if (hint == NoHint) { if ((obj->kind == SimpleInstanceKind) && ((checked_cast(obj))->type == dateClass)) hint = StringHint; else hint = NumberHint; } if (hint == StringHint) { js2val result; if (invokeFunctionOnObject(x, engine->toString_StringAtom, result)) { if (JS2VAL_IS_PRIMITIVE(result)) return result; } if (invokeFunctionOnObject(x, engine->valueOf_StringAtom, result)) { if (JS2VAL_IS_PRIMITIVE(result)) return result; } reportError(Exception::typeError, "DefaultValue failure", engine->errorPos()); } else { js2val result; if (invokeFunctionOnObject(x, engine->valueOf_StringAtom, result)) { if (JS2VAL_IS_PRIMITIVE(result)) return result; } if (invokeFunctionOnObject(x, engine->toString_StringAtom, result)) { if (JS2VAL_IS_PRIMITIVE(result)) return result; } reportError(Exception::typeError, "DefaultValue failure", engine->errorPos()); } return JS2VAL_VOID; } float64 JS2Metadata::convertStringToDouble(const String *str) { bool neg = false; uint32 length = str->length(); if (length == 0) return 0.0; const char16 *numEnd; // if the string begins with '0X' or '0x' (after white space), then // read it as a hex integer. const char16 *strStart = str->data(); const char16 *strEnd = strStart + length; const char16 *str1 = skipWhiteSpace(strStart, strEnd); if (str1 == strEnd) return 0.0; if (*str1 == '-') { neg = true; str1++; } float64 d; if ((*str1 == '0') && ((str1[1] == 'x') || (str1[1] == 'X'))) d = stringToInteger(str1, strEnd, numEnd, 16); else { d = stringToDouble(str1, strEnd, numEnd); if (numEnd == str1) return nan; if (skipWhiteSpace(numEnd, strEnd) != strEnd) return nan; } return (neg) ? -d : d; } // x is not a number float64 JS2Metadata::convertValueToDouble(js2val x) { if (JS2VAL_IS_UNDEFINED(x)) return nan; if (JS2VAL_IS_NULL(x)) return 0; if (JS2VAL_IS_BOOLEAN(x)) return (JS2VAL_TO_BOOLEAN(x)) ? 1.0 : 0.0; if (JS2VAL_IS_STRING(x)) { String *str = JS2VAL_TO_STRING(x); return convertStringToDouble(str); } if (JS2VAL_IS_INACCESSIBLE(x)) reportError(Exception::compileExpressionError, "Inappropriate compile time expression", engine->errorPos()); if (JS2VAL_IS_UNINITIALIZED(x)) reportError(Exception::compileExpressionError, "Inappropriate compile time expression", engine->errorPos()); return toFloat64(toPrimitive(x, NumberHint)); } // x is not a number, convert it to one js2val JS2Metadata::convertValueToGeneralNumber(js2val x) { // XXX Assuming convert to float64, rather than long/ulong return engine->allocNumber(toFloat64(x)); } // x is not an Object, it needs to be wrapped in one js2val JS2Metadata::convertValueToObject(js2val x) { if (JS2VAL_IS_UNDEFINED(x) || JS2VAL_IS_NULL(x) || JS2VAL_IS_SPECIALREF(x)) reportError(Exception::typeError, "Can't convert to Object", engine->errorPos()); if (JS2VAL_IS_STRING(x)) return String_Constructor(this, JS2VAL_NULL, &x, 1); if (JS2VAL_IS_BOOLEAN(x)) return Boolean_Constructor(this, JS2VAL_NULL, &x, 1); if (JS2VAL_IS_NUMBER(x)) return Number_Constructor(this, JS2VAL_NULL, &x, 1); NOT_REACHED("unsupported value type"); return JS2VAL_VOID; } // x is any js2val float64 JS2Metadata::toFloat64(js2val x) { if (JS2VAL_IS_INT(x)) return JS2VAL_TO_INT(x); else if (JS2VAL_IS_DOUBLE(x)) return *JS2VAL_TO_DOUBLE(x); else if (JS2VAL_IS_LONG(x)) { float64 d; JSLL_L2D(d, *JS2VAL_TO_LONG(x)); return d; } else if (JS2VAL_IS_ULONG(x)) { float64 d; JSLL_UL2D(d, *JS2VAL_TO_ULONG(x)); return d; } else if (JS2VAL_IS_FLOAT(x)) return *JS2VAL_TO_FLOAT(x); else return convertValueToDouble(x); } // x is not a bool bool JS2Metadata::convertValueToBoolean(js2val x) { if (JS2VAL_IS_UNDEFINED(x)) return false; if (JS2VAL_IS_NULL(x)) return false; if (JS2VAL_IS_INT(x)) return (JS2VAL_TO_INT(x) != 0); if (JS2VAL_IS_LONG(x) || JS2VAL_IS_ULONG(x)) return (!JSLL_IS_ZERO(x)); if (JS2VAL_IS_FLOAT(x)) { float64 xd = *JS2VAL_TO_FLOAT(x); return ! (JSDOUBLE_IS_POSZERO(xd) || JSDOUBLE_IS_NEGZERO(xd) || JSDOUBLE_IS_NaN(xd)); } if (JS2VAL_IS_DOUBLE(x)) { float64 xd = *JS2VAL_TO_DOUBLE(x); return ! (JSDOUBLE_IS_POSZERO(xd) || JSDOUBLE_IS_NEGZERO(xd) || JSDOUBLE_IS_NaN(xd)); } if (JS2VAL_IS_STRING(x)) { String *str = JS2VAL_TO_STRING(x); return (str->length() != 0); } return true; } // x is not an int int32 JS2Metadata::convertValueToInteger(js2val x) { int32 i; if (JS2VAL_IS_LONG(x)) { JSLL_L2I(i, *JS2VAL_TO_LONG(x)); return i; } if (JS2VAL_IS_ULONG(x)) { JSLL_UL2I(i, *JS2VAL_TO_ULONG(x)); return i; } if (JS2VAL_IS_FLOAT(x)) { float64 f = *JS2VAL_TO_FLOAT(x); return JS2Engine::float64toInt32(f); } if (JS2VAL_IS_DOUBLE(x)) { float64 d = *JS2VAL_TO_DOUBLE(x); return JS2Engine::float64toInt32(d); } float64 d = convertValueToDouble(x); return JS2Engine::float64toInt32(d); } // x is not an int uint32 JS2Metadata::convertValueToUInteger(js2val x) { uint32 i; if (JS2VAL_IS_LONG(x)) { JSLL_L2UI(i, *JS2VAL_TO_LONG(x)); return i; } if (JS2VAL_IS_ULONG(x)) { JSLL_UL2UI(i, *JS2VAL_TO_ULONG(x)); return i; } if (JS2VAL_IS_FLOAT(x)) { float64 f = *JS2VAL_TO_FLOAT(x); return JS2Engine::float64toUInt32(f); } if (JS2VAL_IS_DOUBLE(x)) { float64 d = *JS2VAL_TO_DOUBLE(x); return JS2Engine::float64toUInt32(d); } float64 d = convertValueToDouble(x); return JS2Engine::float64toUInt32(d); } bool defaultReadProperty(JS2Metadata *meta, js2val *base, JS2Class *limit, Multiname *multiname, Environment *env, Phase phase, js2val *rval) { InstanceMember *mBase = meta->findBaseInstanceMember(limit, multiname, ReadAccess); if (mBase) return meta->readInstanceMember(*base, limit, mBase, phase, rval); if (limit != meta->objectType(*base)) return false; Member *m = meta->findCommonMember(base, multiname, ReadAccess, false); if (m == NULL) { if ((env == NULL) && JS2VAL_IS_OBJECT(*base) && (( (JS2VAL_TO_OBJECT(*base)->kind == SimpleInstanceKind) && !checked_cast(JS2VAL_TO_OBJECT(*base))->sealed) || ( (JS2VAL_TO_OBJECT(*base)->kind == PackageKind) && !checked_cast(JS2VAL_TO_OBJECT(*base))->sealed) ) ) { if (phase == CompilePhase) meta->reportError(Exception::compileExpressionError, "Inappropriate compile time expression", meta->engine->errorPos()); else { *rval = JS2VAL_UNDEFINED; return true; } } else return false; } switch (m->memberKind) { case Member::ForbiddenMember: case Member::DynamicVariableMember: case Member::FrameVariableMember: case Member::VariableMember: case Member::ConstructorMethodMember: case Member::SetterMember: case Member::GetterMember: return meta->readLocalMember(checked_cast(m), phase, rval); case Member::InstanceVariableMember: case Member::InstanceMethodMember: case Member::InstanceGetterMember: case Member::InstanceSetterMember: { if (!JS2VAL_IS_OBJECT(*base) || (JS2VAL_TO_OBJECT(*base)->kind != ClassKind) || (env == NULL)) meta->reportError(Exception::referenceError, "Can't read an instance member without supplying an instance", meta->engine->errorPos()); js2val thisVal = env->readImplicitThis(meta); return meta->readInstanceMember(thisVal, meta->objectType(thisVal), checked_cast(m), phase, rval); } default: NOT_REACHED("bad member kind"); return false; } } bool defaultReadPublicProperty(JS2Metadata *meta, js2val *base, JS2Class *limit, const String *name, Phase phase, js2val *rval) { // XXX could speed up by pushing knowledge of single namespace? DEFINE_ROOTKEEPER(rk1, name); Multiname *mn = new Multiname(name, meta->publicNamespace); DEFINE_ROOTKEEPER(rk, mn); return defaultReadProperty(meta, base, limit, mn, NULL, phase, rval); } bool defaultDeletePublic(JS2Metadata *meta, js2val base, JS2Class *limit, const String *name, bool *result) { DEFINE_ROOTKEEPER(rk1, name); // XXX could speed up by pushing knowledge of single namespace? Multiname *mn = new Multiname(name, meta->publicNamespace); DEFINE_ROOTKEEPER(rk, mn); return defaultDeleteProperty(meta, base, limit, mn, NULL, result); } bool defaultWritePublicProperty(JS2Metadata *meta, js2val base, JS2Class *limit, const String *name, bool createIfMissing, js2val newValue) { DEFINE_ROOTKEEPER(rk1, name); // XXX could speed up by pushing knowledge of single namespace? Multiname *mn = new Multiname(name, meta->publicNamespace); DEFINE_ROOTKEEPER(rk, mn); return defaultWriteProperty(meta, base, limit, mn, NULL, createIfMissing, newValue, false); } bool defaultBracketRead(JS2Metadata *meta, js2val *base, JS2Class *limit, js2val indexVal, Phase phase, js2val *rval) { const String *indexStr = meta->toString(indexVal); DEFINE_ROOTKEEPER(rk, indexStr); Multiname *mn = new Multiname(indexStr, meta->publicNamespace); DEFINE_ROOTKEEPER(rk1, mn); return limit->read(meta, base, limit, mn, NULL, phase, rval); } bool arrayClass_WriteProperty(JS2Metadata *meta, js2val base, JS2Class *limit, Multiname *multiname, Environment *env, bool createIfMissing, js2val newValue, bool initFlag) { ASSERT(JS2VAL_IS_OBJECT(base)); JS2Object *obj = JS2VAL_TO_OBJECT(base); bool result = defaultWriteProperty(meta, base, limit, multiname, env, createIfMissing, newValue, false); if (result && (multiname->nsList->size() == 1) && (multiname->nsList->back() == meta->publicNamespace)) { const char16 *numEnd; float64 f = stringToDouble(multiname->name->data(), multiname->name->data() + multiname->name->length(), numEnd); uint32 index = JS2Engine::float64toUInt32(f); char buf[dtosStandardBufferSize]; const char *chrp = doubleToStr(buf, dtosStandardBufferSize, index, dtosStandard, 0); if (widenCString(chrp) == *multiname->name) { uint32 length = getLength(meta, obj); if (index >= length) setLength(meta, obj, index + 1); } } return result; } bool arrayClass_WritePublic(JS2Metadata *meta, js2val base, JS2Class *limit, const String *name, bool createIfMissing, js2val newValue) { DEFINE_ROOTKEEPER(rk1, name); // XXX could speed up by pushing knowledge of single namespace? Multiname *mn = new Multiname(name, meta->publicNamespace); DEFINE_ROOTKEEPER(rk, mn); return arrayClass_WriteProperty(meta, base, limit, mn, meta->env, createIfMissing, newValue, false); } bool defaultWriteProperty(JS2Metadata *meta, js2val base, JS2Class *limit, Multiname *multiname, Environment *env, bool createIfMissing, js2val newValue, bool initFlag) { InstanceMember *mBase = meta->findBaseInstanceMember(limit, multiname, WriteAccess); if (mBase) { meta->writeInstanceMember(base, limit, mBase, newValue); return true; } if (limit != meta->objectType(base)) return false; Member *m = meta->findCommonMember(&base, multiname, WriteAccess, true); if (m == NULL) { // XXX E3 compatibility... JS2Object *baseObj = NULL; DEFINE_ROOTKEEPER(rk, baseObj); if (JS2VAL_IS_PRIMITIVE(base)) { if (meta->cxt.E3compatibility) baseObj = JS2VAL_TO_OBJECT(meta->toObject(base)); } else baseObj = JS2VAL_TO_OBJECT(base); if (createIfMissing && baseObj && ( ((baseObj->kind == SimpleInstanceKind) && !checked_cast(baseObj)->sealed) || ( (baseObj->kind == PackageKind) && !checked_cast(baseObj)->sealed)) ) { QualifiedName qName = multiname->selectPrimaryName(meta); Multiname *mn = new Multiname(qName); DEFINE_ROOTKEEPER(rk, mn); if ( (meta->findBaseInstanceMember(limit, mn, ReadAccess) == NULL) && (meta->findCommonMember(&base, mn, ReadAccess, true) == NULL) ) { meta->createDynamicProperty(baseObj, &qName, newValue, ReadWriteAccess, false, true); return true; } } return false; } switch (m->memberKind) { case Member::ForbiddenMember: case Member::DynamicVariableMember: case Member::FrameVariableMember: case Member::VariableMember: case Member::ConstructorMethodMember: case Member::SetterMember: case Member::GetterMember: return meta->writeLocalMember(checked_cast(m), newValue, initFlag); case Member::InstanceVariableMember: case Member::InstanceMethodMember: case Member::InstanceGetterMember: case Member::InstanceSetterMember: { if ( !JS2VAL_IS_OBJECT(base) || (JS2VAL_TO_OBJECT(base)->kind != ClassKind) || (env == NULL)) meta->reportError(Exception::referenceError, "Can't write an instance member withoutsupplying an instance", meta->engine->errorPos()); js2val thisVal = env->readImplicitThis(meta); meta->writeInstanceMember(thisVal, meta->objectType(thisVal), checked_cast(m), newValue); return true; } default: NOT_REACHED("bad member kind"); return false; } } bool defaultBracketWrite(JS2Metadata *meta, js2val base, JS2Class *limit, js2val indexVal, js2val newValue) { const String *indexStr = meta->toString(indexVal); DEFINE_ROOTKEEPER(rk, indexStr); Multiname *mn = new Multiname(indexStr, meta->publicNamespace); DEFINE_ROOTKEEPER(rk1, mn); return limit->write(meta, base, limit, mn, NULL, true, newValue, false); } bool defaultDeleteProperty(JS2Metadata *meta, js2val base, JS2Class *limit, Multiname *multiname, Environment *env, bool *result) { InstanceMember *mBase = meta->findBaseInstanceMember(limit, multiname, WriteAccess); if (mBase) { *result = false; return true; } if (limit != meta->objectType(base)) return false; Member *m = meta->findCommonMember(&base, multiname, WriteAccess, false); if (m == NULL) return false; switch (m->memberKind) { case Member::ForbiddenMember: meta->reportError(Exception::propertyAccessError, "It is forbidden", meta->engine->errorPos()); return false; case Member::FrameVariableMember: { if (checked_cast(m)->sealed) { *result = false; return true; } goto VariableMemberCommon; } case Member::DynamicVariableMember: { if (checked_cast(m)->sealed) { *result = false; return true; } VariableMemberCommon: // XXX if findCommonMember returned the Binding instead, we wouldn't have to rediscover it here... JS2Object *container = JS2VAL_TO_OBJECT(meta->toObject(base)); LocalBindingMap *lMap; if (container->kind == SimpleInstanceKind) lMap = &checked_cast(container)->localBindings; else lMap = &checked_cast(container)->localBindings; LocalBindingEntry **lbeP = (*lMap)[*multiname->name]; if (lbeP) { while (true) { bool deletedOne = false; for (LocalBindingEntry::NS_Iterator i = (*lbeP)->begin(), end = (*lbeP)->end(); (i != end); i++) { LocalBindingEntry::NamespaceBinding &ns = *i; if (multiname->listContains(ns.first)) { (*lbeP)->bindingList.erase(i); deletedOne = true; break; } } if (!deletedOne) break; } } *result = true; return true; } case Member::VariableMember: case Member::ConstructorMethodMember: case Member::SetterMember: case Member::GetterMember: *result = false; return true; case Member::InstanceVariableMember: case Member::InstanceMethodMember: case Member::InstanceGetterMember: case Member::InstanceSetterMember: if ( (!JS2VAL_IS_OBJECT(base) || (JS2VAL_TO_OBJECT(base)->kind != ClassKind)) || (env == NULL)) { *result = false; return true; } env->readImplicitThis(meta); *result = false; return true; default: NOT_REACHED("bad member kind"); return false; } } bool defaultBracketDelete(JS2Metadata *meta, js2val base, JS2Class *limit, js2val indexVal, bool *result) { const String *indexStr = meta->toString(indexVal); DEFINE_ROOTKEEPER(rk, indexStr); Multiname *mn = new Multiname(indexStr, meta->publicNamespace); DEFINE_ROOTKEEPER(rk1, mn); return limit->deleteProperty(meta, base, limit, mn, NULL, result); } js2val defaultImplicitCoerce(JS2Metadata *meta, js2val newValue, JS2Class *isClass) { if (JS2VAL_IS_NULL(newValue) || meta->objectType(newValue)->isAncestor(isClass) ) return newValue; meta->reportError(Exception::badValueError, "Illegal coercion", meta->engine->errorPos()); return JS2VAL_VOID; } js2val integerImplicitCoerce(JS2Metadata *meta, js2val newValue, JS2Class *isClass) { if (JS2VAL_IS_UNDEFINED(newValue)) return JS2VAL_ZERO; if (JS2VAL_IS_NUMBER(newValue)) { int64 x = meta->engine->checkInteger(newValue); if (JSLL_IS_INT32(x)) { int32 i = 0; JSLL_L2I(i, x); return INT_TO_JS2VAL(i); } } meta->reportError(Exception::badValueError, "Illegal coercion", meta->engine->errorPos()); return JS2VAL_VOID; } js2val defaultIs(JS2Metadata *meta, js2val newValue, JS2Class *isClass) { return BOOLEAN_TO_JS2VAL(meta->objectType(newValue) == isClass); } js2val integerIs(JS2Metadata *meta, js2val newValue, JS2Class *isClass) { bool result = false; if (JS2VAL_IS_NUMBER(newValue)) { float64 f = meta->toFloat64(newValue); if (JSDOUBLE_IS_FINITE(f)) { } } return BOOLEAN_TO_JS2VAL(result); } bool stringClass_BracketRead(JS2Metadata *meta, js2val *base, JS2Class *limit, js2val indexVal, Phase phase, js2val *rval) { if (JS2VAL_IS_INT(indexVal)) { const String *str = NULL; if (JS2VAL_IS_STRING(*base)) { str = JS2VAL_TO_STRING(*base); } else { ASSERT(JS2VAL_IS_OBJECT(*base)); JS2Object *obj = JS2VAL_TO_OBJECT(*base); ASSERT((obj->kind == SimpleInstanceKind) && (checked_cast(obj)->type == meta->stringClass)); StringInstance *a = checked_cast(obj); str = a->mValue; } int32 i = JS2VAL_TO_INT(indexVal); if ((i >= 0) && (i < str->length())) *rval = meta->engine->allocString(&(*str)[i], 1); else *rval = JS2VAL_UNDEFINED; return true; } else return defaultBracketRead(meta, base, limit, indexVal, phase, rval); } }; // namespace MetaData }; // namespace Javascript