/* * Copyright (c) 2017-present Samsung Electronics Co., Ltd * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ #include "api/EscargotPublic.h" using namespace Escargot; #include "gtest/gtest.h" #include static bool stringEndsWith(const std::string& str, const std::string& suffix) { return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix); } static const char32_t offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, static_cast(0xFA082080UL), static_cast(0x82082080UL) }; char32_t readUTF8Sequence(const char*& sequence, bool& valid, int& charlen) { unsigned length; const char sch = *sequence; valid = true; if ((sch & 0x80) == 0) length = 1; else { unsigned char ch2 = static_cast(*(sequence + 1)); if ((sch & 0xE0) == 0xC0 && (ch2 & 0xC0) == 0x80) length = 2; else { unsigned char ch3 = static_cast(*(sequence + 2)); if ((sch & 0xF0) == 0xE0 && (ch2 & 0xC0) == 0x80 && (ch3 & 0xC0) == 0x80) length = 3; else { unsigned char ch4 = static_cast(*(sequence + 3)); if ((sch & 0xF8) == 0xF0 && (ch2 & 0xC0) == 0x80 && (ch3 & 0xC0) == 0x80 && (ch4 & 0xC0) == 0x80) length = 4; else { valid = false; sequence++; return -1; } } } } charlen = length; char32_t ch = 0; switch (length) { case 4: ch += static_cast(*sequence++); ch <<= 6; // Fall through. case 3: ch += static_cast(*sequence++); ch <<= 6; // Fall through. case 2: ch += static_cast(*sequence++); ch <<= 6; // Fall through. case 1: ch += static_cast(*sequence++); } return ch - offsetsFromUTF8[length - 1]; } static std::string evalScript(ContextRef* context, StringRef* str, StringRef* fileName, bool isModule) { if (stringEndsWith(fileName->toStdUTF8String(), "mjs")) { isModule = isModule || true; } auto scriptInitializeResult = context->scriptParser()->initializeScript(str, fileName, isModule); if (!scriptInitializeResult.script) { std::string result("Script parsing error: "); switch (scriptInitializeResult.parseErrorCode) { case Escargot::ErrorObjectRef::Code::SyntaxError: result += "SyntaxError"; break; case Escargot::ErrorObjectRef::Code::EvalError: result += "EvalError"; break; case Escargot::ErrorObjectRef::Code::RangeError: result += "RangeError"; break; case Escargot::ErrorObjectRef::Code::ReferenceError: result += "ReferenceError"; break; case Escargot::ErrorObjectRef::Code::TypeError: result += "TypeError"; break; case Escargot::ErrorObjectRef::Code::URIError: result += "URIError"; break; default: break; } char str[256]; snprintf(str, sizeof(str), ": %s\n", scriptInitializeResult.parseErrorMessage->toStdUTF8String().data()); result += str; return result; } std::string result; auto evalResult = Evaluator::execute(context, [](ExecutionStateRef* state, ScriptRef* script) -> ValueRef* { return script->execute(state); }, scriptInitializeResult.script.get()); char str[256]; if (!evalResult.isSuccessful()) { snprintf(str, sizeof(str), "Uncaught %s:\n", evalResult.resultOrErrorToString(context)->toStdUTF8String().data()); result += str; for (size_t i = 0; i < evalResult.stackTraceData.size(); i++) { snprintf(str, sizeof(str), "%s (%d:%d)\n", evalResult.stackTraceData[i].src->toStdUTF8String().data(), (int)evalResult.stackTraceData[i].loc.line, (int)evalResult.stackTraceData[i].loc.column); result += str; } return result; } result += evalResult.resultOrErrorToString(context)->toStdUTF8String(); while (context->vmInstance()->hasPendingJob()) { context->vmInstance()->executePendingJob(); } return result; } static OptionalRef builtinHelperFileRead(OptionalRef state, const char* fileName, const char* builtinName) { FILE* fp = fopen(fileName, "r"); if (fp) { StringRef* src = StringRef::emptyString(); std::string utf8Str; std::basic_string> str; char buf[512]; bool hasNonLatin1Content = false; size_t readLen; while ((readLen = fread(buf, 1, sizeof buf, fp))) { if (!hasNonLatin1Content) { const char* source = buf; int charlen; bool valid; while (source < buf + readLen) { char32_t ch = readUTF8Sequence(source, valid, charlen); if (ch > 255) { hasNonLatin1Content = true; fseek(fp, 0, SEEK_SET); break; } else { str += (unsigned char)ch; } } } else { utf8Str.append(buf, readLen); } } fclose(fp); if (StringRef::isCompressibleStringEnabled()) { if (state) { if (hasNonLatin1Content) { src = StringRef::createFromUTF8ToCompressibleString(state->context()->vmInstance(), utf8Str.data(), utf8Str.length()); } else { src = StringRef::createFromLatin1ToCompressibleString(state->context()->vmInstance(), str.data(), str.length()); } } else { if (hasNonLatin1Content) { src = StringRef::createFromUTF8(utf8Str.data(), utf8Str.length()); } else { src = StringRef::createFromLatin1(str.data(), str.length()); } } } else { if (hasNonLatin1Content) { src = StringRef::createFromUTF8(utf8Str.data(), utf8Str.length()); } else { src = StringRef::createFromLatin1(str.data(), str.length()); } } return src; } else { char msg[1024]; snprintf(msg, sizeof(msg), "GlobalObject.%s: cannot open file %s", builtinName, fileName); if (state) { state->throwException(URIErrorObjectRef::create(state.get(), StringRef::createFromUTF8(msg, strnlen(msg, sizeof msg)))); } else { puts(msg); } return nullptr; } } class ShellPlatform : public PlatformRef { public: virtual void markJSJobEnqueued(ContextRef* relatedContext) override { // ignore. we always check pending job after eval script } static std::string dirnameOf(const std::string& fname) { size_t pos = fname.find_last_of("/"); if (std::string::npos == pos) { pos = fname.find_last_of("\\/"); } return (std::string::npos == pos) ? "" : fname.substr(0, pos); } static std::string absolutePath(const std::string& referrerPath, const std::string& src) { std::string utf8MayRelativePath = dirnameOf(referrerPath) + "/" + src; auto absPath = realpath(utf8MayRelativePath.data(), nullptr); if (!absPath) { return std::string(); } std::string utf8AbsolutePath = absPath ? absPath : ""; free(absPath); return utf8AbsolutePath; } static std::string absolutePath(const std::string& src) { auto absPath = realpath(src.data(), nullptr); std::string utf8AbsolutePath = absPath; free(absPath); return utf8AbsolutePath; } std::vector>> loadedModules; virtual LoadModuleResult onLoadModule(ContextRef* relatedContext, ScriptRef* whereRequestFrom, StringRef* moduleSrc) override { std::string referrerPath = whereRequestFrom->src()->toStdUTF8String(); for (size_t i = 0; i < loadedModules.size(); i++) { if (std::get<2>(loadedModules[i]) == whereRequestFrom) { referrerPath = std::get<0>(loadedModules[i]); break; } } std::string absPath = absolutePath(referrerPath, moduleSrc->toStdUTF8String()); if (absPath.length() == 0) { std::string s = "Error reading : " + moduleSrc->toStdUTF8String(); return LoadModuleResult(ErrorObjectRef::Code::None, StringRef::createFromUTF8(s.data(), s.length())); } for (size_t i = 0; i < loadedModules.size(); i++) { if (std::get<0>(loadedModules[i]) == absPath && std::get<1>(loadedModules[i]) == relatedContext) { return LoadModuleResult(std::get<2>(loadedModules[i])); } } OptionalRef source = builtinHelperFileRead(nullptr, absPath.data(), ""); if (!source) { std::string s = "Error reading : " + absPath; return LoadModuleResult(ErrorObjectRef::Code::None, StringRef::createFromUTF8(s.data(), s.length())); } auto parseResult = relatedContext->scriptParser()->initializeScript(source.value(), StringRef::createFromUTF8(absPath.data(), absPath.size()), true); if (!parseResult.isSuccessful()) { return LoadModuleResult(parseResult.parseErrorCode, parseResult.parseErrorMessage); } return LoadModuleResult(parseResult.script.get()); } virtual void didLoadModule(ContextRef* relatedContext, OptionalRef referrer, ScriptRef* loadedModule) override { std::string path; if (referrer && loadedModule->src()->length() && loadedModule->src()->charAt(0) != '/') { path = absolutePath(referrer->src()->toStdUTF8String(), loadedModule->src()->toStdUTF8String()); } else { path = absolutePath(loadedModule->src()->toStdUTF8String()); } loadedModules.push_back(std::make_tuple(path, relatedContext, PersistentRefHolder(loadedModule))); } virtual void hostImportModuleDynamically(ContextRef* relatedContext, ScriptRef* referrer, StringRef* src, PromiseObjectRef* promise) override { LoadModuleResult loadedModuleResult = onLoadModule(relatedContext, referrer, src); notifyHostImportModuleDynamicallyResult(relatedContext, referrer, src, promise, loadedModuleResult); } }; PersistentRefHolder g_instance; PersistentRefHolder g_context; int main(int argc, char* argv[]) { testing::InitGoogleTest(&argc, argv); Globals::initialize(); Memory::setGCFrequency(24); ShellPlatform* platform = new ShellPlatform(); PersistentRefHolder g_instance = VMInstanceRef::create(platform); g_instance->setOnVMInstanceDelete([](VMInstanceRef* instance) { delete instance->platform(); }); g_context = ContextRef::create(g_instance.get()); return RUN_ALL_TESTS(); } TEST(EvalScript, Run) { auto s = evalScript(g_context.get(), StringRef::createFromASCII("1 + 1"), StringRef::createFromASCII("test.js"), false); EXPECT_EQ(s, "2"); } TEST(EvalScript, Run2) { auto s = evalScript(g_context.get(), StringRef::createFromASCII("'1' - 1"), StringRef::createFromASCII("test.js"), false); EXPECT_EQ(s, "0"); } TEST(EvalScript, ParseError) { auto s = evalScript(g_context.get(), StringRef::createFromASCII("."), StringRef::createFromASCII("test.js"), false); EXPECT_TRUE(s.find("SyntaxError") != std::string::npos); } TEST(EvalScript, RuntimeError) { auto s = evalScript(g_context.get(), StringRef::createFromASCII("throw 1"), StringRef::createFromASCII("test.js"), false); EXPECT_TRUE(s.find("Uncaught 1") == 0); } TEST(ObjectTemplate, Basic1) { ObjectTemplateRef* tpl = ObjectTemplateRef::create(); EXPECT_TRUE(tpl->isObjectTemplate()); EXPECT_FALSE(tpl->isFunctionTemplate()); int a = 100; int* testPtr = &a; tpl->setInstanceExtraData(testPtr); tpl->set(StringRef::createFromASCII("asdf"), StringRef::createFromASCII("asdfData"), false, false, false); ObjectTemplateRef* another = ObjectTemplateRef::create(); tpl->set(StringRef::createFromASCII("another"), another, false, false, false); ObjectRef* obj = tpl->instantiate(g_context.get()); EXPECT_TRUE(obj->extraData() == testPtr); Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, ObjectRef* obj) -> ValueRef* { auto desc = obj->getOwnPropertyDescriptor(state, StringRef::createFromASCII("asdf")); auto value = desc->asObject()->get(state, StringRef::createFromASCII("value")); EXPECT_TRUE(value->asString()->equalsWithASCIIString("asdfData", 8)); value = desc->asObject()->get(state, StringRef::createFromASCII("writable")); EXPECT_TRUE(value->isFalse()); value = desc->asObject()->get(state, StringRef::createFromASCII("enumerable")); EXPECT_TRUE(value->isFalse()); value = desc->asObject()->get(state, StringRef::createFromASCII("configurable")); EXPECT_TRUE(value->isFalse()); desc = obj->getOwnPropertyDescriptor(state, StringRef::createFromASCII("another")); EXPECT_TRUE(desc->asObject()->get(state, StringRef::createFromASCII("value"))->isObject()); value = desc->asObject()->get(state, StringRef::createFromASCII("writable")); EXPECT_TRUE(value->isFalse()); value = desc->asObject()->get(state, StringRef::createFromASCII("enumerable")); EXPECT_TRUE(value->isFalse()); value = desc->asObject()->get(state, StringRef::createFromASCII("configurable")); EXPECT_TRUE(value->isFalse()); return ValueRef::createUndefined(); }, obj); } TEST(ObjectTemplate, Basic2) { ObjectTemplateRef* tpl = ObjectTemplateRef::create(); auto getter = FunctionTemplateRef::create(AtomicStringRef::emptyAtomicString(), 1, true, true, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, OptionalRef newTarget) -> ValueRef* { return ValueRef::create(12); }); tpl->setAccessorProperty(StringRef::createFromASCII("asdf"), getter, nullptr, false, true); auto getter2 = FunctionTemplateRef::create(AtomicStringRef::emptyAtomicString(), 1, true, true, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, OptionalRef newTarget) -> ValueRef* { return (ValueRef*)thisValue->asObject()->extraData(); }); auto setter = FunctionTemplateRef::create(AtomicStringRef::emptyAtomicString(), 1, true, true, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, OptionalRef newTarget) -> ValueRef* { thisValue->asObject()->setExtraData(argv[0]); return ValueRef::createUndefined(); }); tpl->setAccessorProperty(StringRef::createFromASCII("asdf2"), getter2, setter, false, true); ObjectRef* obj = tpl->instantiate(g_context.get()); Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, ObjectRef* obj) -> ValueRef* { auto desc = obj->getOwnPropertyDescriptor(state, StringRef::createFromASCII("asdf")); auto value = desc->asObject()->get(state, StringRef::createFromASCII("enumerable")); EXPECT_TRUE(value->isFalse()); value = desc->asObject()->get(state, StringRef::createFromASCII("configurable")); EXPECT_TRUE(value->isTrue()); value = desc->asObject()->get(state, StringRef::createFromASCII("get")); EXPECT_TRUE(value->isFunctionObject()); value = desc->asObject()->get(state, StringRef::createFromASCII("set")); EXPECT_TRUE(value->isUndefined()); EXPECT_TRUE(obj->get(state, StringRef::createFromASCII("asdf"))->equalsTo(state, ValueRef::create(12))); obj->set(state, StringRef::createFromASCII("asdf2"), StringRef::createFromASCII("test")); EXPECT_TRUE(obj->get(state, StringRef::createFromASCII("asdf2"))->equalsTo(state, StringRef::createFromASCII("test"))); return ValueRef::createUndefined(); }, obj); } TEST(ObjectTemplate, Basic3) { ObjectTemplateRef* tpl = ObjectTemplateRef::create(); class TestNativeDataAccessorPropertyData : public ObjectRef::NativeDataAccessorPropertyData { public: TestNativeDataAccessorPropertyData() : ObjectRef::NativeDataAccessorPropertyData(false, false, false, nullptr, nullptr) { number = 10; } double number; }; TestNativeDataAccessorPropertyData* data = new TestNativeDataAccessorPropertyData(); data->m_isConfigurable = false; data->m_isEnumerable = false; data->m_isWritable = true; data->m_getter = [](ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, ObjectRef::NativeDataAccessorPropertyData* data) -> ValueRef* { return ValueRef::create(((TestNativeDataAccessorPropertyData*)data)->number); }; data->m_setter = [](ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, ObjectRef::NativeDataAccessorPropertyData* data, ValueRef* setterInputData) -> bool { ((TestNativeDataAccessorPropertyData*)data)->number = setterInputData->toNumber(state) * 2; return true; }; tpl->setNativeDataAccessorProperty(StringRef::createFromASCII("asdf"), data); class TestNativeDataAccessorPropertyData2 : public ObjectRef::NativeDataAccessorPropertyData { public: TestNativeDataAccessorPropertyData2() : ObjectRef::NativeDataAccessorPropertyData(false, false, false, nullptr, nullptr) { child = nullptr; parent = nullptr; value = ValueRef::createUndefined(); getterCallCount = 0; setterCallCount = 0; } ObjectRef* child; ObjectRef* parent; ValueRef* value; int getterCallCount; int setterCallCount; }; TestNativeDataAccessorPropertyData2* data2 = new TestNativeDataAccessorPropertyData2(); data2->m_isConfigurable = false; data2->m_isEnumerable = false; data2->m_isWritable = true; data2->m_getter = [](ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, ObjectRef::NativeDataAccessorPropertyData* data) -> ValueRef* { if (((TestNativeDataAccessorPropertyData2*)data)->getterCallCount == 0) { EXPECT_TRUE(((TestNativeDataAccessorPropertyData2*)data)->parent == self); EXPECT_TRUE(((TestNativeDataAccessorPropertyData2*)data)->child == receiver); } ((TestNativeDataAccessorPropertyData2*)data)->getterCallCount++; return ((TestNativeDataAccessorPropertyData2*)data)->value; }; data2->m_setter = [](ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, ObjectRef::NativeDataAccessorPropertyData* data, ValueRef* setterInputData) -> bool { ((TestNativeDataAccessorPropertyData2*)data)->setterCallCount++; ((TestNativeDataAccessorPropertyData2*)data)->value = ValueRef::create(setterInputData->asNumber() * 2); EXPECT_TRUE(((TestNativeDataAccessorPropertyData2*)data)->parent == self); EXPECT_TRUE(((TestNativeDataAccessorPropertyData2*)data)->child == receiver); return true; }; tpl->setNativeDataAccessorProperty(StringRef::createFromASCII("asdf2"), data2, true); ObjectRef* obj = tpl->instantiate(g_context.get()); Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, ObjectRef* obj, TestNativeDataAccessorPropertyData2* data2, ObjectTemplateRef* tpl) -> ValueRef* { auto desc = obj->getOwnPropertyDescriptor(state, StringRef::createFromASCII("asdf")); auto value = desc->asObject()->get(state, StringRef::createFromASCII("enumerable")); EXPECT_TRUE(value->isFalse()); value = desc->asObject()->get(state, StringRef::createFromASCII("configurable")); EXPECT_TRUE(value->isFalse()); value = desc->asObject()->get(state, StringRef::createFromASCII("writable")); EXPECT_TRUE(value->isTrue()); obj->set(state, StringRef::createFromASCII("asdf"), ValueRef::create(20)); EXPECT_TRUE(obj->get(state, StringRef::createFromASCII("asdf"))->equalsTo(state, ValueRef::create(40))); auto obj2 = ObjectRef::create(state); tpl->installTo(state->context(), obj2); desc = obj2->getOwnPropertyDescriptor(state, StringRef::createFromASCII("asdf")); value = desc->asObject()->get(state, StringRef::createFromASCII("enumerable")); EXPECT_TRUE(value->isFalse()); value = desc->asObject()->get(state, StringRef::createFromASCII("configurable")); EXPECT_TRUE(value->isFalse()); value = desc->asObject()->get(state, StringRef::createFromASCII("writable")); EXPECT_TRUE(value->isTrue()); obj2->set(state, StringRef::createFromASCII("asdf"), ValueRef::create(20)); EXPECT_TRUE(obj2->get(state, StringRef::createFromASCII("asdf"))->equalsTo(state, ValueRef::create(40))); ObjectRef* child = ObjectRef::create(state); child->setPrototype(state, obj); data2->child = child; data2->parent = obj; child->set(state, StringRef::createFromASCII("asdf2"), ValueRef::create(64)); EXPECT_TRUE(data2->setterCallCount == 1); EXPECT_TRUE(data2->value->equalsTo(state, ValueRef::create(128))); child->get(state, StringRef::createFromASCII("asdf2")); EXPECT_TRUE(data2->getterCallCount == 1); EXPECT_TRUE(obj->hasOwnProperty(state, StringRef::createFromASCII("asdf2"))); EXPECT_FALSE(child->hasOwnProperty(state, StringRef::createFromASCII("asdf2"))); auto desc2 = obj->getOwnPropertyDescriptor(state, StringRef::createFromASCII("asdf2")); desc2 = state->context()->globalObject()->jsonStringify()->call(state, ValueRef::createUndefined(), 1, &desc2); EXPECT_TRUE(desc2->toString(state)->toStdUTF8String() == "{\"value\":128,\"writable\":true,\"enumerable\":false,\"configurable\":false}"); return ValueRef::createUndefined(); }, obj, data2, tpl); } TEST(ObjectTemplate, Basic4) { ObjectTemplateRef* tpl = ObjectTemplateRef::create(); ObjectTemplateNamedPropertyHandlerData handler; handler.getter = [](ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, const TemplatePropertyNameRef& propertyName) -> OptionalRef { if (propertyName.value()->isString() && propertyName.value()->asString()->equalsWithASCIIString("aaa", 3)) { return (ValueRef*)self->extraData(); } if (propertyName.value()->isString() && propertyName.value()->asString()->equalsWithASCIIString("ccc", 3)) { return ValueRef::create(555.555); } return OptionalRef(); }; handler.setter = [](ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, const TemplatePropertyNameRef& propertyName, ValueRef* value) -> OptionalRef { if (propertyName.value()->isString() && propertyName.value()->asString()->equalsWithASCIIString("aaa", 3)) { self->setExtraData(value); return OptionalRef(ValueRef::create(true)); } if (propertyName.value()->isString() && propertyName.value()->asString()->equalsWithASCIIString("ccc", 3)) { self->setExtraData(ValueRef::create(123)); return OptionalRef(ValueRef::create(true)); } return OptionalRef(); }; handler.query = [](ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, const TemplatePropertyNameRef& propertyName) -> TemplatePropertyAttribute { if (propertyName.value()->isString() && propertyName.value()->asString()->equalsWithASCIIString("aaa", 3)) { return (TemplatePropertyAttribute)(TemplatePropertyAttribute::TemplatePropertyAttributeWritable | TemplatePropertyAttribute::TemplatePropertyAttributeEnumerable | TemplatePropertyAttribute::TemplatePropertyAttributeConfigurable); } if (propertyName.value()->isString() && propertyName.value()->asString()->equalsWithASCIIString("ccc", 3)) { return (TemplatePropertyAttribute)(TemplatePropertyAttribute::TemplatePropertyAttributeWritable); } return TemplatePropertyAttribute::TemplatePropertyAttributeNotExist; }; handler.enumerator = [](ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data) -> TemplateNamedPropertyHandlerEnumerationCallbackResultVector { TemplateNamedPropertyHandlerEnumerationCallbackResultVector v(2); v[0] = StringRef::createFromASCII("aaa"); v[1] = StringRef::createFromASCII("ccc"); return v; }; handler.deleter = [](ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, const TemplatePropertyNameRef& propertyName) -> OptionalRef { if (propertyName.value()->isString() && propertyName.value()->asString()->equalsWithASCIIString("ddd", 3)) { return OptionalRef(ValueRef::create(false)); } return OptionalRef(); }; handler.descriptor = [](ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, const TemplatePropertyNameRef& propertyName) -> OptionalRef { if (propertyName.value()->isString() && propertyName.value()->asString()->equalsWithASCIIString("eee", 3)) { ObjectRef* desc = ObjectRef::create(state); desc->set(state, StringRef::createFromASCII("value"), ValueRef::createNull()); return desc; } return OptionalRef(); }; tpl->setNamedPropertyHandler(handler); ObjectRef* obj = tpl->instantiate(g_context.get()); obj->setExtraData(ValueRef::create(123)); Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, ObjectRef* obj) -> ValueRef* { EXPECT_TRUE(obj->set(state, StringRef::createFromASCII("aaa"), ValueRef::createNull())); ValueRef* aaa = obj->get(state, StringRef::createFromASCII("aaa")); EXPECT_TRUE(aaa->isNull()); ValueRef* bbb = obj->get(state, StringRef::createFromASCII("bbb")); EXPECT_TRUE(bbb->isUndefined()); EXPECT_TRUE(obj->defineDataProperty(state, StringRef::createFromASCII("ccc"), ValueRef::create(333), true, true, true)); ValueRef* cccResult = obj->get(state, StringRef::createFromASCII("aaa")); EXPECT_TRUE(cccResult->equalsTo(state, ValueRef::create(123))); ValueRef* desc = obj->getOwnPropertyDescriptor(state, StringRef::createFromASCII("aaa")); ValueRef* json = state->context()->globalObject()->jsonStringify()->call(state, ValueRef::createUndefined(), 1, &desc); EXPECT_TRUE(json->toString(state)->toStdUTF8String() == "{\"value\":123,\"writable\":true,\"enumerable\":true,\"configurable\":true}"); desc = obj->getOwnPropertyDescriptor(state, StringRef::createFromASCII("ccc")); json = state->context()->globalObject()->jsonStringify()->call(state, ValueRef::createUndefined(), 1, &desc); EXPECT_TRUE(json->toString(state)->toStdUTF8String() == "{\"value\":555.555,\"writable\":true,\"enumerable\":false,\"configurable\":false}"); int pcnt = 0; obj->enumerateObjectOwnProperies(state, [&](ExecutionStateRef* state, ValueRef* propertyName, bool isWritable, bool isEnumerable, bool isConfigurable)->bool { if (pcnt == 0) { EXPECT_TRUE(propertyName->toString(state)->toStdUTF8String() == "aaa"); EXPECT_TRUE(isWritable); EXPECT_TRUE(isEnumerable); EXPECT_TRUE(isConfigurable); } else if (pcnt == 1) { EXPECT_TRUE(propertyName->toString(state)->toStdUTF8String() == "ccc"); EXPECT_TRUE(isWritable); EXPECT_TRUE(!isEnumerable); EXPECT_TRUE(!isConfigurable); } pcnt++; return true; }); EXPECT_TRUE(pcnt == 2); EXPECT_TRUE(obj->deleteOwnProperty(state, StringRef::createFromASCII("fff"))); EXPECT_FALSE(obj->deleteOwnProperty(state, StringRef::createFromASCII("ddd"))); auto descObj = obj->getOwnPropertyDescriptor(state, StringRef::createFromASCII("eee")); EXPECT_TRUE(descObj->isObject()); EXPECT_TRUE(descObj->asObject()->get(state, StringRef::createFromASCII("value"))->isNull()); return ValueRef::createUndefined(); }, obj); ObjectTemplateRef* tpl2 = ObjectTemplateRef::create(); ObjectTemplateNamedPropertyHandlerData handler2; handler2.getter = [](ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, const TemplatePropertyNameRef& propertyName) -> OptionalRef { return OptionalRef(); }; handler2.definer = [](ExecutionStateRef* state, ObjectRef* self, ValueRef* receiver, void* data, const TemplatePropertyNameRef& propertyName, const ObjectPropertyDescriptorRef& desc) -> OptionalRef { if (propertyName.value()->isString() && propertyName.value()->asString()->equalsWithASCIIString("ttt", 3)) { return OptionalRef(ValueRef::create(false)); } return OptionalRef(); }; tpl2->setNamedPropertyHandler(handler2); obj = tpl2->instantiate(g_context.get()); Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, ObjectRef* obj) -> ValueRef* { EXPECT_FALSE(obj->defineDataProperty(state, StringRef::createFromASCII("ttt"), ValueRef::create(111), false, false, false)); return ValueRef::createUndefined(); }, obj); } TEST(FunctionTemplate, Basic1) { auto ft = FunctionTemplateRef::create(AtomicStringRef::create(g_context.get(), "asdf"), 2, true, true, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, OptionalRef newTarget) -> ValueRef* { if (newTarget) { EXPECT_TRUE(thisValue->isObject()); return ValueRef::createNull(); } else { EXPECT_TRUE(argc == 1); EXPECT_TRUE(argv[0]->toString(state)->toStdUTF8String() == "123"); return argv[0]; } }); ft->setName(AtomicStringRef::create(g_context.get(), "asdf2")); int a = 100; int* testPtr = &a; ft->setInstanceExtraData(testPtr); EXPECT_FALSE(ft->isObjectTemplate()); EXPECT_TRUE(ft->isFunctionTemplate()); FunctionObjectRef* fn = ft->instantiate(g_context.get())->asFunctionObject(); EXPECT_TRUE(fn->extraData() == testPtr); // same instance on same context EXPECT_EQ(fn, ft->instantiate(g_context.get())->asFunctionObject()); auto r = Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, FunctionObjectRef* fn) -> ValueRef* { ValueRef* arr[1] = { ValueRef::create(123) }; EXPECT_TRUE(fn->call(state, ValueRef::createUndefined(), 1, arr)->equalsTo(state, ValueRef::create(123))); ValueRef* ret = fn->construct(state, 0, nullptr); EXPECT_TRUE(ret->isNull()); ValueRef* name = fn->getOwnProperty(state, StringRef::createFromASCII("name", 4)); EXPECT_TRUE(name->asString()->equalsWithASCIIString("asdf2", 5)); return ValueRef::createUndefined(); }, fn); EXPECT_TRUE(r.isSuccessful()); } TEST(FunctionTemplate, Basic2) { auto ft = FunctionTemplateRef::create(AtomicStringRef::create(g_context.get(), "asdf"), 2, true, false, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, OptionalRef newTarget) -> ValueRef* { EXPECT_TRUE(argc == 1); return argv[0]; }); int a = 100; int* testPtr = &a; ft->setInstanceExtraData(testPtr); EXPECT_FALSE(ft->isObjectTemplate()); EXPECT_TRUE(ft->isFunctionTemplate()); FunctionObjectRef* fn = ft->instantiate(g_context.get())->asFunctionObject(); EXPECT_TRUE(fn->extraData() == testPtr); // same instance on same context EXPECT_EQ(fn, ft->instantiate(g_context.get())->asFunctionObject()); Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, FunctionObjectRef* fn) -> ValueRef* { ValueRef* arr[1] = { ValueRef::create(123) }; EXPECT_TRUE(fn->call(state, ValueRef::createUndefined(), 1, arr)->equalsTo(state, ValueRef::create(123))); return ValueRef::createUndefined(); }, fn); } TEST(FunctionTemplate, Basic3) { auto ft = FunctionTemplateRef::create(AtomicStringRef::create(g_context.get(), "parent"), 0, true, true, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, OptionalRef newTarget) -> ValueRef* { return ValueRef::createUndefined(); }); ft->prototypeTemplate()->set(StringRef::createFromASCII("asdf1"), ValueRef::create(1), true, true, true); auto ftchild = FunctionTemplateRef::create(AtomicStringRef::create(g_context.get(), "asdf"), 2, true, true, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, OptionalRef newTarget) -> ValueRef* { return ValueRef::create(123); }); auto ftchildobj = ftchild->instanceTemplate(); ftchildobj->set(StringRef::createFromASCII("asdf"), ValueRef::create(0), true, true, true); ftchild->prototypeTemplate()->set(StringRef::createFromASCII("asdf2"), ValueRef::create(2), true, true, true); ftchild->inherit(ft); Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, FunctionTemplateRef* ftchild) -> ValueRef* { ObjectRef* ref = ftchild->instanceTemplate()->instantiate(state->context()); EXPECT_TRUE(ref->get(state, StringRef::createFromASCII("asdf"))->equalsTo(state, ValueRef::create(0))); EXPECT_TRUE(ref->hasOwnProperty(state, StringRef::createFromASCII("asdf"))); EXPECT_TRUE(ref->get(state, StringRef::createFromASCII("asdf1"))->equalsTo(state, ValueRef::create(1))); EXPECT_TRUE(ref->get(state, StringRef::createFromASCII("asdf2"))->equalsTo(state, ValueRef::create(2))); return ValueRef::createUndefined(); }, ftchild); } TEST(BackingStore, Basic1) { Evaluator::execute(g_context.get(), [](ExecutionStateRef* state) -> ValueRef* { auto bs = BackingStoreRef::create(1024); EXPECT_FALSE(bs->isShared()); EXPECT_TRUE(bs->byteLength() == 1024); auto abo = ArrayBufferObjectRef::create(state); abo->attachBuffer(bs); EXPECT_TRUE(bs->data() == abo->rawBuffer()); EXPECT_TRUE(bs->isShared()); EXPECT_TRUE(abo->byteLength() == 1024); abo->detachArrayBuffer(); EXPECT_TRUE(abo->byteLength() == 0); EXPECT_TRUE(abo->isDetachedBuffer()); EXPECT_TRUE(abo->rawBuffer() == nullptr); EXPECT_FALSE(abo->backingStore().hasValue()); abo->allocateBuffer(state, 1024); EXPECT_TRUE(abo->byteLength() == 1024); // there is no error bs = BackingStoreRef::create(calloc(1024, 1), 1024, [](void* data, size_t length, void* deleterData) { free(data); }, nullptr); EXPECT_FALSE(bs->isShared()); EXPECT_TRUE(bs->byteLength() == 1024); bs = nullptr; return ValueRef::createUndefined(); }); }