Files
ark_js_runtime/ecmascript/require/js_cjs_module.cpp
T
DaiH eee606c334 Support CommonJS's module export function
------------------------------------------
    1. Support CommonJS's module export function
    2. Related test cases
    issue:https://gitee.com/openharmony/ark_js_runtime/issues/I5590V

Signed-off-by: DaiH <daihuina1@huawei.com>
Change-Id: I18968f6a6b971fbe833d91ef98813964cc8292c9
2022-06-08 10:59:06 +08:00

189 lines
9.3 KiB
C++

/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "js_cjs_module.h"
#include "ecmascript/interpreter/interpreter-inl.h"
#include "ecmascript/interpreter/slow_runtime_stub.h"
#include "ecmascript/require/js_cjs_module_cache.h"
#include "ecmascript/require/js_require_manager.h"
#include "ecmascript/jspandafile/js_pandafile.h"
#include "ecmascript/jspandafile/js_pandafile_executor.h"
#include "ecmascript/jspandafile/js_pandafile_manager.h"
namespace panda::ecmascript {
void JSCjsModule::InitializeModule(JSThread *thread, JSHandle<JSCjsModule> &module,
JSHandle<JSTaggedValue> &filename, JSHandle<JSTaggedValue> &dirname)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> dirKey(factory->NewFromASCII("path"));
SlowRuntimeStub::StObjByName(thread, module.GetTaggedValue(), dirKey.GetTaggedValue(),
dirname.GetTaggedValue());
JSHandle<JSTaggedValue> filenameKey(factory->NewFromASCII("filename"));
SlowRuntimeStub::StObjByName(thread, module.GetTaggedValue(), filenameKey.GetTaggedValue(),
filename.GetTaggedValue());
module->SetFilename(thread, filename.GetTaggedValue());
module->SetPath(thread, dirname.GetTaggedValue());
return;
}
JSHandle<JSTaggedValue> JSCjsModule::SearchFromModuleCache(JSThread *thread, JSHandle<JSTaggedValue> &filename)
{
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> moduleObj(env->GetCjsModuleFunction());
JSHandle<JSTaggedValue> cacheName = globalConst->GetHandledCjsCacheString();
JSTaggedValue modCache = SlowRuntimeStub::LdObjByName(thread, moduleObj.GetTaggedValue(),
cacheName.GetTaggedValue(),
false,
JSTaggedValue::Undefined());
JSHandle<CjsModuleCache> moduleCache = JSHandle<CjsModuleCache>(thread, modCache);
if (moduleCache->ContainsModule(filename.GetTaggedValue())) {
JSHandle<JSCjsModule> cachedModule = JSHandle<JSCjsModule>(thread,
moduleCache->GetModule(filename.GetTaggedValue()));
JSHandle<JSTaggedValue> exportsName = globalConst->GetHandledCjsExportsString();
JSTaggedValue cachedExports = SlowRuntimeStub::LdObjByName(thread, cachedModule.GetTaggedValue(),
exportsName.GetTaggedValue(),
false,
JSTaggedValue::Undefined());
return JSHandle<JSTaggedValue>(thread, cachedExports);
}
return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Hole());
}
void JSCjsModule::PutIntoCache(JSThread *thread, JSHandle<JSCjsModule> &module, JSHandle<JSTaggedValue> &filename)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> moduleObj(env->GetCjsModuleFunction());
JSHandle<JSTaggedValue> cacheName = globalConst->GetHandledCjsCacheString();
JSTaggedValue modCache = SlowRuntimeStub::LdObjByName(thread, moduleObj.GetTaggedValue(),
cacheName.GetTaggedValue(),
false,
JSTaggedValue::Undefined());
JSHandle<CjsModuleCache> moduleCache = JSHandle<CjsModuleCache>(thread, modCache);
JSHandle<JSTaggedValue> moduleHandle = JSHandle<JSTaggedValue>::Cast(module);
JSHandle<CjsModuleCache> newCache = CjsModuleCache::PutIfAbsent(thread, moduleCache, filename,
moduleHandle);
SlowRuntimeStub::StObjByName(thread, moduleObj.GetTaggedValue(), cacheName.GetTaggedValue(),
newCache.GetTaggedValue());
}
JSHandle<JSTaggedValue> JSCjsModule::Load(JSThread *thread, JSHandle<EcmaString> &request)
{
[[maybe_unused]] EcmaHandleScope handleScope(thread);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
// Get local jsPandaFile's dirPath
JSMutableHandle<JSTaggedValue> parent(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> dirname(thread, JSTaggedValue::Undefined());
const JSPandaFile *jsPandaFile = EcmaInterpreter::GetNativeCallPandafile(thread);
JSRequireManager::ResolveCurrentPath(thread, parent, dirname, jsPandaFile);
// Get filename from Callback
JSHandle<EcmaString> filenameStr = ResolveFilename(thread, dirname.GetTaggedValue(),
request.GetTaggedValue());
JSHandle<JSTaggedValue> filename = JSHandle<JSTaggedValue>::Cast(filenameStr);
// Search from Module.cache
JSHandle<JSTaggedValue> maybeCachedExports = SearchFromModuleCache(thread, filename);
if (!maybeCachedExports->IsHole()) {
return maybeCachedExports;
}
// Don't get required exports from cache, execute required JSPandaFile.
// module = new Module(), which belongs to required JSPandaFile.
JSHandle<JSCjsModule> module = factory->NewCjsModule();
InitializeModule(thread, module, filename, dirname);
PutIntoCache(thread, module, filename);
// Execute required JSPandaFile
RequireExecution(thread, JSHandle<EcmaString>::Cast(filename));
// Search from Module.cache after execution.
JSHandle<JSTaggedValue> cachedExports = SearchFromModuleCache(thread, filename);
if (cachedExports->IsHole()) {
LOG_ECMA(ERROR) << "CJS REQUIRE FAIL : Can not obtain module, after executing required jsPandaFile";
UNREACHABLE();
}
return cachedExports;
}
void JSCjsModule::RequireExecution(JSThread *thread, const JSHandle<EcmaString> &moduleFileName)
{
CString moduleFilenameStr = ConvertToString(moduleFileName.GetTaggedValue());
const JSPandaFile *jsPandaFile =
JSPandaFileManager::GetInstance()->LoadJSPandaFile(moduleFilenameStr, JSPandaFile::ENTRY_MAIN_FUNCTION);
if (jsPandaFile == nullptr) {
LOG_ECMA(ERROR) << "open jsPandaFile " << moduleFilenameStr << " error";
UNREACHABLE();
}
JSPandaFileExecutor::Execute(thread, jsPandaFile);
}
JSHandle<EcmaString> JSCjsModule::ResolveFilename(JSThread *thread, JSTaggedValue dirname, JSTaggedValue request)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
ResolvePathCallback resolvePathCallback = thread->GetEcmaVM()->GetResolvePathCallback();
if (resolvePathCallback == nullptr) {
JSHandle<EcmaString> nativeRequireName = ResolveFilenameFromNative(thread, dirname, request);
return nativeRequireName;
}
std::string modDirname = std::string(ConvertToString(EcmaString::Cast(dirname.GetHeapObject())));
std::string modFile = std::string(ConvertToString(EcmaString::Cast(request.GetHeapObject())));
std::string callbackRequireName = resolvePathCallback(modDirname, modFile);
CString fullname = callbackRequireName.c_str();
return factory->NewFromUtf8(fullname);
}
JSHandle<EcmaString> JSCjsModule::ResolveFilenameFromNative(JSThread *thread, JSTaggedValue dirname,
JSTaggedValue request)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
CString dirnameStr = ConvertToString(EcmaString::Cast(dirname.GetHeapObject()));
CString requestStr = ConvertToString(EcmaString::Cast(request.GetHeapObject()));
if (requestStr.find("./") == 0) {
requestStr = requestStr.substr(2); // 2 : delete './'
}
int suffixEnd = static_cast<int>(requestStr.find_last_of('.'));
if (suffixEnd == -1) {
RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread);
}
CString fullname;
if (requestStr[0] == '/') { // absolute FilePath
fullname = requestStr.substr(0, suffixEnd) + ".abc";
} else {
int pos = static_cast<int>(dirnameStr.find_last_of('/'));
if (pos == -1) {
RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread);
}
fullname = dirnameStr.substr(0, pos + 1) + requestStr.substr(0, suffixEnd) + ".abc";
}
return factory->NewFromUtf8(fullname);
}
JSTaggedValue JSCjsModule::Require(JSThread *thread, JSHandle<EcmaString> &request,
[[maybe_unused]]JSHandle<JSCjsModule> &parent,
[[maybe_unused]]bool isMain)
{
Load(thread, request);
return JSTaggedValue::Undefined();
}
} // namespace panda::ecmascript