Skip to content

Commit

Permalink
Implement JSON module parsing
Browse files Browse the repository at this point in the history
Currently only the import statement is supported

Signed-off-by: Zoltan Herczeg [email protected]
  • Loading branch information
Zoltan Herczeg authored and clover2123 committed Sep 13, 2022
1 parent b992df9 commit 3a0b874
Show file tree
Hide file tree
Showing 12 changed files with 199 additions and 85 deletions.
16 changes: 12 additions & 4 deletions src/api/EscargotPublic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@ class PlatformBridge : public Platform {
m_platform->markJSJobEnqueued(toRef(relatedContext));
}

virtual LoadModuleResult onLoadModule(Context* relatedContext, Script* whereRequestFrom, String* moduleSrc) override
virtual LoadModuleResult onLoadModule(Context* relatedContext, Script* whereRequestFrom, String* moduleSrc, ModuleType type) override
{
LoadModuleResult result;
auto refResult = m_platform->onLoadModule(toRef(relatedContext), toRef(whereRequestFrom), toRef(moduleSrc));
auto refResult = m_platform->onLoadModule(toRef(relatedContext), toRef(whereRequestFrom), toRef(moduleSrc), static_cast<Escargot::PlatformRef::ModuleType>(type));

result.script = toImpl(refResult.script.get());
result.errorMessage = toImpl(refResult.errorMessage);
Expand All @@ -163,9 +163,9 @@ class PlatformBridge : public Platform {
}
}

virtual void hostImportModuleDynamically(Context* relatedContext, Script* referrer, String* src, PromiseObject* promise) override
virtual void hostImportModuleDynamically(Context* relatedContext, Script* referrer, String* src, ModuleType type, PromiseObject* promise) override
{
m_platform->hostImportModuleDynamically(toRef(relatedContext), toRef(referrer), toRef(src), toRef(promise));
m_platform->hostImportModuleDynamically(toRef(relatedContext), toRef(referrer), toRef(src), static_cast<Escargot::PlatformRef::ModuleType>(type), toRef(promise));
}

virtual bool canBlockExecution(Context* relatedContext) override
Expand Down Expand Up @@ -4355,6 +4355,14 @@ ScriptParserRef::InitializeFunctionScriptResult ScriptParserRef::initializeFunct
return result;
}

ScriptParserRef::InitializeScriptResult ScriptParserRef::initializeJSONModule(StringRef* sourceCode, StringRef* srcName)
{
ScriptParserRef::InitializeScriptResult result;

result.script = toRef(toImpl(this)->initializeJSONModule(toImpl(sourceCode), toImpl(srcName)));
return result;
}

bool ScriptRef::isModule()
{
return toImpl(this)->isModule();
Expand Down
13 changes: 10 additions & 3 deletions src/api/EscargotPublic.h
Original file line number Diff line number Diff line change
Expand Up @@ -2046,6 +2046,8 @@ class ESCARGOT_EXPORT ScriptParserRef {
// convert the input body source into a function and parse it
// generate Script and FunctionObject
InitializeFunctionScriptResult initializeFunctionScript(StringRef* sourceName, AtomicStringRef* functionName, size_t argumentCount, ValueRef** argumentNameArray, ValueRef* functionBody);
// parse the input JSON data and return the result (Script)
InitializeScriptResult initializeJSONModule(StringRef* sourceCode, StringRef* srcName);
};

class ESCARGOT_EXPORT ScriptRef {
Expand Down Expand Up @@ -2105,6 +2107,11 @@ class ESCARGOT_EXPORT PlatformRef {
virtual void markJSJobEnqueued(ContextRef* relatedContext) = 0;

// Module
enum ModuleType {
ModuleES,
ModuleJSON,
};

// client needs cache module map<absolute_module_path, ScriptRef*>
struct ESCARGOT_EXPORT LoadModuleResult {
LoadModuleResult(ScriptRef* result);
Expand All @@ -2114,13 +2121,13 @@ class ESCARGOT_EXPORT PlatformRef {
StringRef* errorMessage;
ErrorObjectRef::Code errorCode;
};
virtual LoadModuleResult onLoadModule(ContextRef* relatedContext, ScriptRef* whereRequestFrom, StringRef* moduleSrc) = 0;
virtual LoadModuleResult onLoadModule(ContextRef* relatedContext, ScriptRef* whereRequestFrom, StringRef* moduleSrc, ModuleType type) = 0;
virtual void didLoadModule(ContextRef* relatedContext, OptionalRef<ScriptRef> whereRequestFrom, ScriptRef* loadedModule) = 0;

// Dynamic Import
virtual void hostImportModuleDynamically(ContextRef* relatedContext, ScriptRef* referrer, StringRef* src, PromiseObjectRef* promise)
virtual void hostImportModuleDynamically(ContextRef* relatedContext, ScriptRef* referrer, StringRef* src, ModuleType type, PromiseObjectRef* promise)
{
notifyHostImportModuleDynamicallyResult(relatedContext, referrer, src, promise, onLoadModule(relatedContext, referrer, src));
notifyHostImportModuleDynamicallyResult(relatedContext, referrer, src, promise, onLoadModule(relatedContext, referrer, src, type));
}
void notifyHostImportModuleDynamicallyResult(ContextRef* relatedContext, ScriptRef* referrer, StringRef* src, PromiseObjectRef* promise, LoadModuleResult loadModuleResult);

Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/ByteCodeInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3608,7 +3608,7 @@ NEVER_INLINE void ByteCodeInterpreter::callFunctionComplexCase(ExecutionState& s
innerPromiseCapability.m_promise->asPromiseObject()->then(state, onFulfilled, onRejected);

Global::platform()->hostImportModuleDynamically(byteCodeBlock->m_codeBlock->context(),
referencingScriptOrModule, specifierString, innerPromiseCapability.m_promise->asPromiseObject());
referencingScriptOrModule, specifierString, Platform::ModuleES, innerPromiseCapability.m_promise->asPromiseObject());

// Return promiseCapability.[[Promise]].
registerFile[code->m_resultIndex] = promiseCapability.m_promise;
Expand Down
35 changes: 25 additions & 10 deletions src/parser/Script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
#include "runtime/Global.h"
#include "runtime/Environment.h"
#include "runtime/EnvironmentRecord.h"
#include "runtime/Platform.h"
#include "runtime/ErrorObject.h"
#include "runtime/ExtendedNativeFunctionObject.h"
#include "runtime/JSON.h"
#include "runtime/SandBox.h"
#include "runtime/ScriptFunctionObject.h"
#include "runtime/ScriptAsyncFunctionObject.h"
Expand Down Expand Up @@ -80,9 +80,9 @@ Context* Script::context()
return m_topCodeBlock->context();
}

Script* Script::loadModuleFromScript(ExecutionState& state, String* src)
Script* Script::loadModuleFromScript(ExecutionState& state, ModuleRequest& request)
{
Platform::LoadModuleResult result = Global::platform()->onLoadModule(context(), this, src);
Platform::LoadModuleResult result = Global::platform()->onLoadModule(context(), this, request.m_specifier, request.m_type);
if (!result.script) {
ErrorObject::throwBuiltinError(state, (ErrorObject::Code)result.errorCode, result.errorMessage->toNonGCUTF8StringData().data());
}
Expand All @@ -104,7 +104,7 @@ size_t Script::moduleRequestsLength()
String* Script::moduleRequest(size_t i)
{
ASSERT(isModule());
return m_moduleData->m_requestedModules[i];
return m_moduleData->m_requestedModules[i].m_specifier;
}

Value Script::moduleInstantiate(ExecutionState& state)
Expand Down Expand Up @@ -233,6 +233,10 @@ Script::ResolveExportResult Script::resolveExport(ExecutionState& state, AtomicS
}
}

if (m_topCodeBlock == nullptr) {
return Script::ResolveExportResult(Script::ResolveExportResult::Null);
}

// For each ExportEntry Record e in module.[[IndirectExportEntries]], do
auto& indirectExportEntries = m_moduleData->m_indirectExportEntries;
for (size_t i = 0; i < indirectExportEntries.size(); i++) {
Expand Down Expand Up @@ -706,7 +710,7 @@ Script::ModuleExecutionResult Script::innerModuleLinking(ExecutionState& state,
size_t rmLength = moduleRequestsLength();
for (size_t i = 0; i < rmLength; i++) {
// Let requiredModule be ! HostResolveImportedModule(module, required).
Script* requiredModule = loadModuleFromScript(state, moduleRequest(i));
Script* requiredModule = loadModuleFromScript(state, m_moduleData->m_requestedModules[i]);
// NOTE: Instantiate must be completed successfully prior to invoking this method, so every requested module is guaranteed to resolve successfully.
// Set index to ? innerModuleInstantiation(requiredModule, stack, index).
auto result = requiredModule->innerModuleLinking(state, stack, index);
Expand Down Expand Up @@ -864,7 +868,7 @@ Script::ModuleExecutionResult Script::innerModuleEvaluation(ExecutionState& stat
size_t rmLength = moduleRequestsLength();
for (size_t i = 0; i < rmLength; i++) {
// Let requiredModule be ! HostResolveImportedModule(module, required).
Script* requiredModule = loadModuleFromScript(state, moduleRequest(i));
Script* requiredModule = loadModuleFromScript(state, m_moduleData->m_requestedModules[i]);
// NOTE: Instantiate must be completed successfully prior to invoking this method, so every requested module is guaranteed to resolve successfully.
// Set index to ? InnerModuleEvaluation(requiredModule, stack, index).
auto result = requiredModule->innerModuleEvaluation(state, stack, index);
Expand Down Expand Up @@ -914,8 +918,19 @@ Script::ModuleExecutionResult Script::innerModuleEvaluation(ExecutionState& stat
}
}

// If module.[[PendingAsyncDependencies]] > 0, set module.[[AsyncEvaluating]] to true.
if (md->m_pendingAsyncDependencies.hasValue() && md->m_pendingAsyncDependencies.value() > 0) {
if (m_topCodeBlock == nullptr) {
// Synthetic module evaluation
ModuleEnvironmentRecord* moduleRecord = md->m_moduleRecord;
moduleRecord->createBinding(state, state.context()->staticStrings().stringStarDefaultStar, false, false, false);

try {
moduleRecord->initializeBinding(state, state.context()->staticStrings().stringStarDefaultStar, JSON::parse(state, sourceCode(), Value()));
} catch (const Value& e) {
md->m_evaluationError = EncodedValue(e);
}

} else if (md->m_pendingAsyncDependencies.hasValue() && md->m_pendingAsyncDependencies.value() > 0) {
// If module.[[PendingAsyncDependencies]] > 0, set module.[[AsyncEvaluating]] to true.
md->m_asyncEvaluating = true;
} else if (m_topCodeBlock->isAsync()) {
// Otherwise, if module.[[Async]] is true, perform ! ExecuteAsyncModule(module).
Expand Down Expand Up @@ -1018,15 +1033,15 @@ Value Script::moduleInitializeEnvironment(ExecutionState& state)
if (resolution.m_type == Script::ResolveExportResult::Null) {
StringBuilder builder;
builder.appendString("The requested module '");
builder.appendString(in.m_moduleRequest);
builder.appendString(in.m_moduleRequest.m_specifier);
builder.appendString("' does not provide an export named '");
builder.appendString(in.m_localName.string());
builder.appendString("'");
return ErrorObject::createBuiltinError(state, ErrorObject::Code::SyntaxError, builder.finalize(&state)->toNonGCUTF8StringData().data());
} else if (resolution.m_type == Script::ResolveExportResult::Ambiguous) {
StringBuilder builder;
builder.appendString("The requested module '");
builder.appendString(in.m_moduleRequest);
builder.appendString(in.m_moduleRequest.m_specifier);
builder.appendString("' does not provide an export named '");
builder.appendString(in.m_localName.string());
builder.appendString("' correctly");
Expand Down
38 changes: 27 additions & 11 deletions src/parser/Script.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include "runtime/Value.h"
#include "runtime/PromiseObject.h"
#include "runtime/Platform.h"

namespace Escargot {

Expand All @@ -36,28 +37,43 @@ class Script : public gc {
friend class ModuleNamespaceObject;

public:
struct ModuleRequest {
// Specifier (name) of the module (usually a file system entry)
String* m_specifier;
// Type of the module (cached value of [[Assertions]] where items.[[Key]] is "type")
Platform::ModuleType m_type;

ModuleRequest()
: m_specifier(nullptr)
, m_type(Platform::ModuleES)
{
}

ModuleRequest(String* specifier, Platform::ModuleType type)
: m_specifier(specifier)
, m_type(type)
{
}
};
typedef Vector<ModuleRequest, GCUtil::gc_malloc_allocator<ModuleRequest>> ModuleRequestVector;

// https://tc39.es/ecma262/#importentry-record
struct ImportEntry {
// [[ModuleRequest]] String String value of the ModuleSpecifier of the ImportDeclaration.
String* m_moduleRequest;
// [[ModuleRequest]] ModuleRequest The value of the ModuleSpecifier and its assertions of the ImportDeclaration.
ModuleRequest m_moduleRequest;
// [[ImportName]] String The name under which the desired binding is exported by the module identified by [[ModuleRequest]]. The value "*" indicates that the import request is for the target module’s namespace object.
AtomicString m_importName;
// [[LocalName]] String The name that is used to locally access the imported value from within the importing module.
AtomicString m_localName;

ImportEntry()
: m_moduleRequest(String::emptyString)
{
}
};
typedef Vector<ImportEntry, GCUtil::gc_malloc_allocator<ImportEntry>> ImportEntryVector;

// https://tc39.es/ecma262/#exportentry-record
struct ExportEntry {
// [[ExportName]] String The name used to export this binding by this module.
Optional<AtomicString> m_exportName;
// [[ModuleRequest]] String | null The String value of the ModuleSpecifier of the ExportDeclaration. null if the ExportDeclaration does not have a ModuleSpecifier.
Optional<String*> m_moduleRequest;
// [[ModuleRequest]] ModuleRequest | null The value of the ModuleSpecifier of the ExportDeclaration. null if the ExportDeclaration does not have a ModuleSpecifier.
Optional<ModuleRequest> m_moduleRequest;
// [[ImportName]] String | null The name under which the desired binding is exported by the module identified by [[ModuleRequest]]. null if the ExportDeclaration does not have a ModuleSpecifier. "*" indicates that the export request is for all exported bindings.
Optional<AtomicString> m_importName;
// [[LocalName]] String | null The name that is used to locally access the exported value from within the importing module. null if the exported value is not locally accessible from within the module.
Expand Down Expand Up @@ -105,7 +121,7 @@ class Script : public gc {
// [[DFSAncestorIndex]]
Optional<uint32_t> m_dfsAncestorIndex;
// [[RequestedModules]] is same with moduleRequests
StringVector m_requestedModules;
ModuleRequestVector m_requestedModules;

// + https://tc39.es/proposal-top-level-await/#sec-cyclic-module-records
// [[CycleRoot]]
Expand Down Expand Up @@ -209,7 +225,7 @@ class Script : public gc {

private:
Value executeLocal(ExecutionState& state, Value thisValue, InterpretedCodeBlock* parentCodeBlock, bool isStrictModeOutside = false, bool isEvalCodeOnFunction = false);
Script* loadModuleFromScript(ExecutionState& state, String* src);
Script* loadModuleFromScript(ExecutionState& state, ModuleRequest& request);
void loadExternalModule(ExecutionState& state);
Value executeModule(ExecutionState& state, Optional<Script*> referrer);
struct ResolveExportResult {
Expand Down
20 changes: 20 additions & 0 deletions src/parser/ScriptParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,26 @@ void ScriptParser::generateFunctionByteCode(ExecutionState& state, InterpretedCo
GC_enable();
}

Script* ScriptParser::initializeJSONModule(String* source, String* srcName)
{
Script::ModuleData* moduleData = new Script::ModuleData();

Script::ExportEntry entry;
entry.m_exportName = m_context->staticStrings().stringDefault;
entry.m_localName = m_context->staticStrings().stringStarDefaultStar;

moduleData->m_localExportEntries.push_back(entry);

Script* script = new Script(srcName, source, moduleData, false, 0);

ModuleEnvironmentRecord* moduleRecord = new ModuleEnvironmentRecord(script);
moduleData->m_moduleRecord = moduleRecord;

moduleData->m_cycleRoot = script;
moduleData->m_status = Script::ModuleData::Linked;
return script;
}

#ifdef ESCARGOT_DEBUGGER

void ScriptParser::recursivelyGenerateChildrenByteCode(InterpretedCodeBlock* parent)
Expand Down
2 changes: 2 additions & 0 deletions src/parser/ScriptParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ class ScriptParser : public gc {
return initializeScript(nullptr, 0, source, srcName, nullptr, isModule);
}

Script* initializeJSONModule(String* source, String* srcName);

Context* context() const { return m_context; }

void generateFunctionByteCode(ExecutionState& state, InterpretedCodeBlock* codeBlock, size_t stackSizeRemain);
Expand Down
Loading

0 comments on commit 3a0b874

Please sign in to comment.