зеркало из https://github.com/mozilla/gecko-dev.git
Bug 930414 - Add ModuleObject and CompileModule() function r=shu
This commit is contained in:
Родитель
0ecb01460a
Коммит
adf4b1ae87
|
@ -0,0 +1,61 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "builtin/ModuleObject.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// ModuleObject
|
||||
|
||||
const Class ModuleObject::class_ = {
|
||||
"Module",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) |
|
||||
JSCLASS_IS_ANONYMOUS |
|
||||
JSCLASS_IMPLEMENTS_BARRIERS,
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* setProperty */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* resolve */
|
||||
nullptr, /* mayResolve */
|
||||
nullptr, /* convert */
|
||||
nullptr, /* finalize */
|
||||
nullptr, /* call */
|
||||
nullptr, /* hasInstance */
|
||||
nullptr, /* construct */
|
||||
ModuleObject::trace
|
||||
};
|
||||
|
||||
/* static */ ModuleObject*
|
||||
ModuleObject::create(ExclusiveContext* cx)
|
||||
{
|
||||
return NewBuiltinClassInstance<ModuleObject>(cx, TenuredObject);
|
||||
}
|
||||
|
||||
void
|
||||
ModuleObject::init(HandleScript script)
|
||||
{
|
||||
initReservedSlot(ScriptSlot, PrivateValue(script));
|
||||
}
|
||||
|
||||
JSScript*
|
||||
ModuleObject::script() const
|
||||
{
|
||||
return static_cast<JSScript*>(getReservedSlot(ScriptSlot).toPrivate());
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ModuleObject::trace(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
ModuleObject& module = obj->as<ModuleObject>();
|
||||
JSScript* script = module.script();
|
||||
TraceManuallyBarrieredEdge(trc, &script, "Module script");
|
||||
module.setReservedSlot(ScriptSlot, PrivateValue(script));
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef builtin_ModuleObject_h
|
||||
#define builtin_ModuleObject_h
|
||||
|
||||
#include "vm/NativeObject.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class ModuleObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
ScriptSlot = 0,
|
||||
SlotCount
|
||||
};
|
||||
|
||||
static const Class class_;
|
||||
|
||||
static ModuleObject* create(ExclusiveContext* cx);
|
||||
void init(HandleScript script);
|
||||
|
||||
JSScript* script() const;
|
||||
|
||||
private:
|
||||
static void trace(JSTracer* trc, JSObject* obj);
|
||||
};
|
||||
|
||||
typedef Rooted<ModuleObject*> RootedModuleObject;
|
||||
typedef Handle<ModuleObject*> HandleModuleObject;
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* builtin_ModuleObject_h */
|
|
@ -10,6 +10,7 @@
|
|||
#include "jsscript.h"
|
||||
|
||||
#include "asmjs/AsmJSLink.h"
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "frontend/BytecodeEmitter.h"
|
||||
#include "frontend/FoldConstants.h"
|
||||
#include "frontend/NameFunctions.h"
|
||||
|
@ -57,6 +58,7 @@ class MOZ_STACK_CLASS BytecodeCompiler
|
|||
void setSourceArgumentsNotIncluded();
|
||||
|
||||
JSScript* compileScript(HandleObject scopeChain, HandleScript evalCaller);
|
||||
ModuleObject* compileModule();
|
||||
bool compileFunctionBody(MutableHandleFunction fun, Handle<PropertyNameVector> formals,
|
||||
GeneratorKind generatorKind);
|
||||
|
||||
|
@ -606,6 +608,50 @@ BytecodeCompiler::compileScript(HandleObject scopeChain, HandleScript evalCaller
|
|||
return script;
|
||||
}
|
||||
|
||||
ModuleObject* BytecodeCompiler::compileModule()
|
||||
{
|
||||
MOZ_ASSERT(!enclosingStaticScope);
|
||||
|
||||
if (!createSourceAndParser())
|
||||
return nullptr;
|
||||
|
||||
if (!createScript())
|
||||
return nullptr;
|
||||
|
||||
Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
|
||||
if (!module)
|
||||
return nullptr;
|
||||
|
||||
module->init(script);
|
||||
|
||||
ParseNode* pn = parser->standaloneModule(module);
|
||||
if (!pn)
|
||||
return nullptr;
|
||||
|
||||
if (!NameFunctions(cx, pn) ||
|
||||
!maybeSetDisplayURL(parser->tokenStream) ||
|
||||
!maybeSetSourceMap(parser->tokenStream))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
script->bindings = pn->pn_modulebox->bindings;
|
||||
|
||||
if (!createEmitter(pn->pn_modulebox) ||
|
||||
!emitter->emitModuleScript(pn->pn_body))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!maybeCompleteCompressSource())
|
||||
return nullptr;
|
||||
|
||||
parser->handler.freeTree(pn);
|
||||
|
||||
MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending());
|
||||
return module;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeCompiler::compileFunctionBody(MutableHandleFunction fun,
|
||||
Handle<PropertyNameVector> formals,
|
||||
|
@ -721,6 +767,22 @@ frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject sco
|
|||
return compiler.compileScript(scopeChain, evalCaller);
|
||||
}
|
||||
|
||||
ModuleObject*
|
||||
frontend::CompileModule(JSContext* cx, HandleObject obj,
|
||||
const ReadOnlyCompileOptions& optionsInput,
|
||||
SourceBufferHolder& srcBuf)
|
||||
{
|
||||
MOZ_ASSERT(srcBuf.get());
|
||||
|
||||
CompileOptions options(cx, optionsInput);
|
||||
options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
|
||||
options.setIsRunOnce(true);
|
||||
|
||||
BytecodeCompiler compiler(cx, &cx->tempLifoAlloc(), options, srcBuf, nullptr,
|
||||
TraceLogger_ParserCompileModule);
|
||||
return compiler.compileModule();
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace js {
|
|||
|
||||
class LazyScript;
|
||||
class LifoAlloc;
|
||||
class ModuleObject;
|
||||
class ScriptSourceObject;
|
||||
class ScopeObject;
|
||||
struct SourceCompressionTask;
|
||||
|
@ -30,6 +31,10 @@ CompileScript(ExclusiveContext* cx, LifoAlloc* alloc,
|
|||
SourceBufferHolder& srcBuf, JSString* source_ = nullptr,
|
||||
SourceCompressionTask* extraSct = nullptr);
|
||||
|
||||
ModuleObject *
|
||||
CompileModule(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
|
||||
SourceBufferHolder &srcBuf);
|
||||
|
||||
bool
|
||||
CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);
|
||||
|
||||
|
|
|
@ -2212,6 +2212,10 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
|||
*answer = false;
|
||||
return true;
|
||||
|
||||
case PNK_MODULE:
|
||||
*answer = false;
|
||||
return true;
|
||||
|
||||
// Generator expressions have no side effects on their own.
|
||||
case PNK_GENEXP:
|
||||
MOZ_ASSERT(pn->isArity(PN_LIST));
|
||||
|
@ -3500,6 +3504,55 @@ BytecodeEmitter::emitFunctionScript(ParseNode* body)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitModuleScript(ParseNode* body)
|
||||
{
|
||||
if (!updateLocalsToFrameSlots())
|
||||
return false;
|
||||
|
||||
/*
|
||||
* IonBuilder has assumptions about what may occur immediately after
|
||||
* script->main (e.g., in the case of destructuring params). Thus, put the
|
||||
* following ops into the range [script->code, script->main). Note:
|
||||
* execution starts from script->code, so this has no semantic effect.
|
||||
*/
|
||||
|
||||
ModuleBox* modulebox = sc->asModuleBox();
|
||||
|
||||
// Link the module and the script to each other, so that StaticScopeIter
|
||||
// may walk the scope chain of currently compiling scripts.
|
||||
JSScript::linkToModuleFromEmitter(cx, script, modulebox);
|
||||
|
||||
if (!emitTree(body))
|
||||
return false;
|
||||
|
||||
// Always end the script with a JSOP_RETRVAL. Some other parts of the codebase
|
||||
// depend on this opcode, e.g. InterpreterRegs::setToEndOfScript.
|
||||
if (!emit1(JSOP_RETRVAL))
|
||||
return false;
|
||||
|
||||
// If all locals are aliased, the frame's block slots won't be used, so we
|
||||
// can set numBlockScoped = 0. This is nice for generators as it ensures
|
||||
// nfixed == 0, so we don't have to initialize any local slots when resuming
|
||||
// a generator.
|
||||
if (sc->allLocalsAliased())
|
||||
script->bindings.setAllLocalsAliased();
|
||||
|
||||
if (!JSScript::fullyInitFromEmitter(cx, script, this))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Since modules are only run once. Mark the script so that initializers
|
||||
* created within it may be given more precise types.
|
||||
*/
|
||||
script->setTreatAsRunOnce();
|
||||
MOZ_ASSERT(!script->hasRunOnce());
|
||||
|
||||
tellDebuggerAboutCompiledScript(cx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::maybeEmitVarDecl(JSOp prologueOp, ParseNode* pn, jsatomid* result)
|
||||
{
|
||||
|
|
|
@ -320,6 +320,9 @@ struct BytecodeEmitter
|
|||
// Emit function code for the tree rooted at body.
|
||||
bool emitFunctionScript(ParseNode* body);
|
||||
|
||||
// Emit module code for the tree rooted at body.
|
||||
bool emitModuleScript(ParseNode* body);
|
||||
|
||||
// If op is JOF_TYPESET (see the type barriers comment in TypeInference.h),
|
||||
// reserve a type set to store its result.
|
||||
void checkTypeSet(JSOp op);
|
||||
|
|
|
@ -100,6 +100,10 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
|||
*result = false;
|
||||
return true;
|
||||
|
||||
case PNK_MODULE:
|
||||
*result = false;
|
||||
return true;
|
||||
|
||||
// Statements with no sub-components at all.
|
||||
case PNK_NOP: // induced by function f() {} function f() {}
|
||||
case PNK_DEBUGGER:
|
||||
|
@ -1114,6 +1118,17 @@ ComputeBinary(ParseNodeKind kind, double left, double right)
|
|||
return int32_t((kind == PNK_LSH) ? uint32_t(i) << j : i >> j);
|
||||
}
|
||||
|
||||
static bool
|
||||
FoldModule(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_MODULE));
|
||||
MOZ_ASSERT(node->isArity(PN_CODE));
|
||||
|
||||
ParseNode*& moduleBody = node->pn_body;
|
||||
MOZ_ASSERT(moduleBody);
|
||||
return Fold(cx, &moduleBody, parser, false);
|
||||
}
|
||||
|
||||
static bool
|
||||
FoldBinaryArithmetic(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
|
||||
bool inGenexpLambda)
|
||||
|
@ -1787,6 +1802,9 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
|||
case PNK_FUNCTION:
|
||||
return FoldFunction(cx, pn, parser, inGenexpLambda);
|
||||
|
||||
case PNK_MODULE:
|
||||
return FoldModule(cx, pn, parser);
|
||||
|
||||
case PNK_SUB:
|
||||
case PNK_STAR:
|
||||
case PNK_LSH:
|
||||
|
|
|
@ -635,7 +635,7 @@ class FullParseHandler
|
|||
inline void setLastFunctionArgumentDestructuring(ParseNode* funcpn, ParseNode* pn);
|
||||
|
||||
ParseNode* newFunctionDefinition() {
|
||||
return new_<CodeNode>(pos());
|
||||
return new_<CodeNode>(PNK_FUNCTION, pos());
|
||||
}
|
||||
void setFunctionBody(ParseNode* pn, ParseNode* kid) {
|
||||
pn->pn_body = kid;
|
||||
|
@ -652,6 +652,14 @@ class FullParseHandler
|
|||
pn->pn_funbox->setDerivedClassConstructor();
|
||||
}
|
||||
|
||||
ParseNode* newModule() {
|
||||
return new_<CodeNode>(PNK_MODULE, pos());
|
||||
}
|
||||
void setModuleBox(ParseNode* pn, ModuleBox* modulebox) {
|
||||
MOZ_ASSERT(pn->isKind(PNK_MODULE));
|
||||
pn->pn_modulebox = modulebox;
|
||||
}
|
||||
|
||||
ParseNode* newLexicalScope(ObjectBox* blockBox) {
|
||||
return new_<LexicalScopeNode>(blockBox, pos());
|
||||
}
|
||||
|
|
|
@ -337,7 +337,7 @@ class NameResolver
|
|||
if (cur == nullptr)
|
||||
return true;
|
||||
|
||||
MOZ_ASSERT(cur->isKind(PNK_FUNCTION) == cur->isArity(PN_CODE));
|
||||
MOZ_ASSERT((cur->isKind(PNK_FUNCTION) || cur->isKind(PNK_MODULE)) == cur->isArity(PN_CODE));
|
||||
if (cur->isKind(PNK_FUNCTION)) {
|
||||
RootedAtom prefix2(cx);
|
||||
if (!resolveFun(cur, prefix, &prefix2))
|
||||
|
@ -767,6 +767,7 @@ class NameResolver
|
|||
break;
|
||||
|
||||
case PNK_FUNCTION:
|
||||
case PNK_MODULE:
|
||||
MOZ_ASSERT(cur->isArity(PN_CODE));
|
||||
if (!resolve(cur->pn_body, prefix))
|
||||
return false;
|
||||
|
|
|
@ -521,6 +521,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
return PushNameNodeChildren(pn, stack);
|
||||
|
||||
case PNK_FUNCTION:
|
||||
case PNK_MODULE:
|
||||
return PushCodeNodeChildren(pn, stack);
|
||||
|
||||
case PNK_LIMIT: // invalid sentinel value
|
||||
|
@ -1151,6 +1152,13 @@ ObjectBox::asFunctionBox()
|
|||
return static_cast<FunctionBox*>(this);
|
||||
}
|
||||
|
||||
ModuleBox*
|
||||
ObjectBox::asModuleBox()
|
||||
{
|
||||
MOZ_ASSERT(isModuleBox());
|
||||
return static_cast<ModuleBox*>(this);
|
||||
}
|
||||
|
||||
void
|
||||
ObjectBox::trace(JSTracer* trc)
|
||||
{
|
||||
|
@ -1162,6 +1170,9 @@ ObjectBox::trace(JSTracer* trc)
|
|||
funbox->bindings.trace(trc);
|
||||
if (funbox->enclosingStaticScope_)
|
||||
TraceRoot(trc, &funbox->enclosingStaticScope_, "funbox-enclosingStaticScope");
|
||||
} else if (box->isModuleBox()) {
|
||||
ModuleBox* modulebox = box->asModuleBox();
|
||||
modulebox->bindings.trace(trc);
|
||||
}
|
||||
box = box->traceLink;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ struct ParseContext;
|
|||
|
||||
class FullParseHandler;
|
||||
class FunctionBox;
|
||||
class ModuleBox;
|
||||
class ObjectBox;
|
||||
|
||||
// A packed ScopeCoordinate for use in the frontend during bytecode
|
||||
|
@ -118,6 +119,7 @@ class PackedScopeCoordinate
|
|||
F(NULL) \
|
||||
F(THIS) \
|
||||
F(FUNCTION) \
|
||||
F(MODULE) \
|
||||
F(IF) \
|
||||
F(SWITCH) \
|
||||
F(CASE) \
|
||||
|
@ -639,6 +641,7 @@ class ParseNode
|
|||
JSAtom* atom; /* lexical name or label atom */
|
||||
ObjectBox* objbox; /* block or regexp object */
|
||||
FunctionBox* funbox; /* function object */
|
||||
ModuleBox* modulebox; /* module object */
|
||||
};
|
||||
union {
|
||||
ParseNode* expr; /* module or function body, var
|
||||
|
@ -662,6 +665,7 @@ class ParseNode
|
|||
} pn_u;
|
||||
|
||||
#define pn_modulebox pn_u.name.modulebox
|
||||
#define pn_objbox pn_u.name.objbox
|
||||
#define pn_funbox pn_u.name.funbox
|
||||
#define pn_body pn_u.name.expr
|
||||
#define pn_scopecoord pn_u.name.scopeCoord
|
||||
|
@ -1072,15 +1076,17 @@ struct ListNode : public ParseNode
|
|||
|
||||
struct CodeNode : public ParseNode
|
||||
{
|
||||
explicit CodeNode(const TokenPos& pos)
|
||||
: ParseNode(PNK_FUNCTION, JSOP_NOP, PN_CODE, pos)
|
||||
CodeNode(ParseNodeKind kind, const TokenPos& pos)
|
||||
: ParseNode(kind, JSOP_NOP, PN_CODE, pos)
|
||||
{
|
||||
MOZ_ASSERT(kind == PNK_FUNCTION || kind == PNK_MODULE);
|
||||
MOZ_ASSERT(!pn_body);
|
||||
MOZ_ASSERT(!pn_funbox);
|
||||
MOZ_ASSERT(!pn_objbox);
|
||||
MOZ_ASSERT(pn_dflags == 0);
|
||||
pn_scopecoord.makeFree();
|
||||
}
|
||||
|
||||
public:
|
||||
#ifdef DEBUG
|
||||
void dump(int indent);
|
||||
#endif
|
||||
|
@ -1687,6 +1693,8 @@ class ObjectBox
|
|||
ObjectBox(JSObject* object, ObjectBox* traceLink);
|
||||
bool isFunctionBox() { return object->is<JSFunction>(); }
|
||||
FunctionBox* asFunctionBox();
|
||||
bool isModuleBox() { return object->is<ModuleObject>(); }
|
||||
ModuleBox* asModuleBox();
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "jstypes.h"
|
||||
|
||||
#include "asmjs/AsmJSValidate.h"
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "frontend/BytecodeCompiler.h"
|
||||
#include "frontend/FoldConstants.h"
|
||||
#include "frontend/ParseMaps.h"
|
||||
|
@ -409,12 +410,9 @@ AppendPackedBindings(const ParseContext<ParseHandler>* pc, const DeclVector& vec
|
|||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
|
||||
LifoAlloc& alloc,
|
||||
ParseContext<ParseHandler>::generateBindings(ExclusiveContext* cx, TokenStream& ts, LifoAlloc& alloc,
|
||||
MutableHandle<Bindings> bindings) const
|
||||
{
|
||||
MOZ_ASSERT(sc->isFunctionBox());
|
||||
MOZ_ASSERT(args_.length() < ARGNO_LIMIT);
|
||||
MOZ_ASSERT(vars_.length() + bodyLevelLexicals_.length() < LOCALNO_LIMIT);
|
||||
|
||||
/*
|
||||
|
@ -453,6 +451,28 @@ ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext* cx, Token
|
|||
packedBindings);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
|
||||
LifoAlloc& alloc,
|
||||
MutableHandle<Bindings> bindings) const
|
||||
{
|
||||
MOZ_ASSERT(sc->isFunctionBox());
|
||||
MOZ_ASSERT(args_.length() < ARGNO_LIMIT);
|
||||
return generateBindings(cx, ts, alloc, bindings);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
ParseContext<ParseHandler>::generateModuleBindings(ExclusiveContext* cx, TokenStream& ts,
|
||||
LifoAlloc& alloc,
|
||||
MutableHandle<Bindings> bindings) const
|
||||
{
|
||||
MOZ_ASSERT(sc->isModuleBox());
|
||||
MOZ_ASSERT(args_.length() == 0);
|
||||
return generateBindings(cx, ts, alloc, bindings);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
|
||||
|
@ -688,6 +708,49 @@ Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun,
|
|||
return funbox;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
ModuleBox::ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
|
||||
ParseContext<ParseHandler>* outerpc)
|
||||
: ObjectBox(module, traceListHead),
|
||||
SharedContext(cx, Directives(true), extraWarnings),
|
||||
bindings()
|
||||
{}
|
||||
|
||||
template <typename ParseHandler>
|
||||
ModuleBox*
|
||||
Parser<ParseHandler>::newModuleBox(Node pn, HandleModuleObject module)
|
||||
{
|
||||
MOZ_ASSERT(module);
|
||||
|
||||
/*
|
||||
* We use JSContext.tempLifoAlloc to allocate parsed objects and place them
|
||||
* on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
|
||||
* arenas containing the entries must be alive until we are done with
|
||||
* scanning, parsing and code generation for the whole module.
|
||||
*/
|
||||
ParseContext<ParseHandler>* outerpc = nullptr;
|
||||
ModuleBox* modbox =
|
||||
alloc.new_<ModuleBox>(context, traceListHead, module, outerpc);
|
||||
if (!modbox) {
|
||||
ReportOutOfMemory(context);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
traceListHead = modbox;
|
||||
if (pn)
|
||||
handler.setModuleBox(pn, modbox);
|
||||
|
||||
return modbox;
|
||||
}
|
||||
|
||||
template <>
|
||||
ModuleBox*
|
||||
Parser<SyntaxParseHandler>::newModuleBox(Node pn, HandleModuleObject module)
|
||||
{
|
||||
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
void
|
||||
Parser<ParseHandler>::trace(JSTracer* trc)
|
||||
|
@ -785,6 +848,57 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName* name, Node pn)
|
|||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::standaloneModule(HandleModuleObject module)
|
||||
{
|
||||
MOZ_ASSERT(checkOptionsCalled);
|
||||
|
||||
Node mn = handler.newModule();
|
||||
if (!mn)
|
||||
return null();
|
||||
|
||||
ModuleBox* modulebox = newModuleBox(mn, module);
|
||||
if (!modulebox)
|
||||
return null();
|
||||
handler.setModuleBox(mn, modulebox);
|
||||
|
||||
ParseContext<FullParseHandler> modulepc(this, pc, mn, modulebox, nullptr, 0);
|
||||
if (!modulepc.init(*this))
|
||||
return null();
|
||||
|
||||
ParseNode* pn = statements(YieldIsKeyword);
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
MOZ_ASSERT(pn->isKind(PNK_STATEMENTLIST));
|
||||
mn->pn_body = pn;
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
MOZ_ASSERT(tt == TOK_EOF);
|
||||
|
||||
if (!FoldConstants(context, &pn, this))
|
||||
return null();
|
||||
|
||||
Rooted<Bindings> bindings(context, modulebox->bindings);
|
||||
if (!modulepc.generateModuleBindings(context, tokenStream, alloc, &bindings))
|
||||
return null();
|
||||
modulebox->bindings = bindings;
|
||||
|
||||
MOZ_ASSERT(mn->pn_modulebox == modulebox);
|
||||
return mn;
|
||||
}
|
||||
|
||||
template <>
|
||||
SyntaxParseHandler::Node
|
||||
Parser<SyntaxParseHandler>::standaloneModule(HandleModuleObject module)
|
||||
{
|
||||
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode*
|
||||
Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
class ModuleObject;
|
||||
class StaticFunctionBoxScopeObject;
|
||||
class StaticModuleBoxScopeObject;
|
||||
|
||||
namespace frontend {
|
||||
|
||||
|
@ -197,23 +199,32 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
|||
void updateDecl(JSAtom* atom, Node newDecl);
|
||||
|
||||
/*
|
||||
* After a function body has been parsed, the parser generates the
|
||||
* function's "bindings". Bindings are a data-structure, ultimately stored
|
||||
* in the compiled JSScript, that serve three purposes:
|
||||
* After a function body or module has been parsed, the parser generates the
|
||||
* code's "bindings". Bindings are a data-structure, ultimately stored in
|
||||
* the compiled JSScript, that serve three purposes:
|
||||
* - After parsing, the ParseContext is destroyed and 'decls' along with
|
||||
* it. Mostly, the emitter just uses the binding information stored in
|
||||
* the use/def nodes, but the emitter occasionally needs 'bindings' for
|
||||
* various scope-related queries.
|
||||
* - Bindings provide the initial js::Shape to use when creating a dynamic
|
||||
* scope object (js::CallObject) for the function. This shape is used
|
||||
* during dynamic name lookup.
|
||||
* scope object (js::CallObject). This shape is used during dynamic name
|
||||
* lookup.
|
||||
* - Sometimes a script's bindings are accessed at runtime to retrieve the
|
||||
* contents of the lexical scope (e.g., from the debugger).
|
||||
*/
|
||||
private:
|
||||
bool generateBindings(ExclusiveContext* cx, TokenStream& ts, LifoAlloc& alloc,
|
||||
MutableHandle<Bindings> bindings) const;
|
||||
|
||||
public:
|
||||
bool generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
|
||||
LifoAlloc& alloc,
|
||||
MutableHandle<Bindings> bindings) const;
|
||||
|
||||
bool generateModuleBindings(ExclusiveContext* cx, TokenStream& ts,
|
||||
LifoAlloc& alloc,
|
||||
MutableHandle<Bindings> bindings) const;
|
||||
|
||||
private:
|
||||
ParseContext** parserPC; /* this points to the Parser's active pc
|
||||
and holds either |this| or one of
|
||||
|
@ -300,8 +311,13 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
|||
// function f1() { function f2() { } }
|
||||
// if (cond) { function f3() { if (cond) { function f4() { } } } }
|
||||
//
|
||||
bool atBodyLevel() { return !innermostStmt(); }
|
||||
bool atGlobalLevel() { return atBodyLevel() && !sc->isFunctionBox() && !innermostScopeStmt(); }
|
||||
bool atBodyLevel() {
|
||||
return !innermostStmt();
|
||||
}
|
||||
|
||||
bool atGlobalLevel() {
|
||||
return atBodyLevel() && !sc->isFunctionBox() && !innermostScopeStmt();
|
||||
}
|
||||
|
||||
// True if this is the ParseContext for the body of a function created by
|
||||
// the Function constructor.
|
||||
|
@ -490,6 +506,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
|||
return newFunctionBox(fn, fun, outerpc, directives, generatorKind, enclosing);
|
||||
}
|
||||
|
||||
ModuleBox* newModuleBox(Node pn, HandleModuleObject module);
|
||||
|
||||
/*
|
||||
* Create a new function object given a name (which is optional if this is
|
||||
* a function expression).
|
||||
|
@ -542,6 +560,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
|||
|
||||
bool maybeParseDirective(Node list, Node pn, bool* cont);
|
||||
|
||||
Node standaloneModule(Handle<ModuleObject*> module);
|
||||
|
||||
// Parse a function, given only its body. Used for the Function and
|
||||
// Generator constructors.
|
||||
Node standaloneFunctionBody(HandleFunction fun, Handle<PropertyNameVector> formals,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "jsscript.h"
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "frontend/ParseMaps.h"
|
||||
#include "frontend/ParseNode.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
|
@ -215,8 +216,11 @@ class SharedContext
|
|||
void computeInWith(JSObject* staticScope);
|
||||
|
||||
virtual ObjectBox* toObjectBox() { return nullptr; }
|
||||
inline bool isFunctionBox() { return toObjectBox() && toObjectBox()->isFunctionBox(); }
|
||||
bool isObjectBox() { return toObjectBox() != nullptr; }
|
||||
bool isFunctionBox() { return isObjectBox() && toObjectBox()->isFunctionBox(); }
|
||||
inline FunctionBox* asFunctionBox();
|
||||
bool isModuleBox() { return isObjectBox() && toObjectBox()->isModuleBox(); }
|
||||
inline ModuleBox* asModuleBox();
|
||||
|
||||
bool allowNewTarget() const { return allowNewTarget_; }
|
||||
bool allowSuperProperty() const { return allowSuperProperty_; }
|
||||
|
@ -368,6 +372,20 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
}
|
||||
};
|
||||
|
||||
class ModuleBox : public ObjectBox, public SharedContext
|
||||
{
|
||||
public:
|
||||
Bindings bindings;
|
||||
|
||||
template <typename ParseHandler>
|
||||
ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
|
||||
ParseContext<ParseHandler>* pc);
|
||||
|
||||
ObjectBox* toObjectBox() override { return this; }
|
||||
ModuleObject* module() const { return &object->as<ModuleObject>(); }
|
||||
JSObject* staticScope() const override { return module(); }
|
||||
};
|
||||
|
||||
inline FunctionBox*
|
||||
SharedContext::asFunctionBox()
|
||||
{
|
||||
|
@ -375,6 +393,13 @@ SharedContext::asFunctionBox()
|
|||
return static_cast<FunctionBox*>(this);
|
||||
}
|
||||
|
||||
inline ModuleBox*
|
||||
SharedContext::asModuleBox()
|
||||
{
|
||||
MOZ_ASSERT(isModuleBox());
|
||||
return static_cast<ModuleBox*>(this);
|
||||
}
|
||||
|
||||
|
||||
// In generators, we treat all locals as aliased so that they get stored on the
|
||||
// heap. This way there is less information to copy off the stack when
|
||||
|
|
|
@ -361,6 +361,7 @@ AssertRootMarkingPhase(JSTracer* trc)
|
|||
D(GlobalObject*) \
|
||||
D(JSObject*) \
|
||||
D(JSFunction*) \
|
||||
D(ModuleObject*) \
|
||||
D(NestedScopeObject*) \
|
||||
D(PlainObject*) \
|
||||
D(SavedFrame*) \
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
// Exercise shell parseModule function.
|
||||
|
||||
parseModule("");
|
||||
parseModule("const foo = 1;");
|
|
@ -2652,6 +2652,25 @@ JSScript::linkToFunctionFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScrip
|
|||
fun->setScript(script);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
JSScript::linkToModuleFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
|
||||
js::frontend::ModuleBox* modulebox)
|
||||
{
|
||||
script->funHasExtensibleScope_ = false;
|
||||
script->funNeedsDeclEnvObject_ = false;
|
||||
script->needsHomeObject_ = false;
|
||||
script->isDerivedClassConstructor_ = false;
|
||||
script->funLength_ = 0;
|
||||
|
||||
script->isGeneratorExp_ = false;
|
||||
script->setGeneratorKind(NotGenerator);
|
||||
|
||||
// Link the module and the script to each other, so that StaticScopeIter
|
||||
// may walk the scope chain of currently compiling scripts.
|
||||
RootedModuleObject module(cx, modulebox->module());
|
||||
script->setModule(module);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, BytecodeEmitter* bce)
|
||||
{
|
||||
|
@ -3595,6 +3614,9 @@ JSScript::traceChildren(JSTracer* trc)
|
|||
if (functionNonDelazifying())
|
||||
TraceEdge(trc, &function_, "function");
|
||||
|
||||
if (module_)
|
||||
TraceEdge(trc, &module_, "module");
|
||||
|
||||
if (enclosingStaticScope_)
|
||||
TraceEdge(trc, &enclosingStaticScope_, "enclosingStaticScope");
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "jsopcode.h"
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "gc/Barrier.h"
|
||||
#include "gc/Rooting.h"
|
||||
#include "jit/IonCode.h"
|
||||
|
@ -46,15 +47,16 @@ class BreakpointSite;
|
|||
class BindingIter;
|
||||
class Debugger;
|
||||
class LazyScript;
|
||||
class NestedScopeObject;
|
||||
class RegExpObject;
|
||||
struct SourceCompressionTask;
|
||||
class Shape;
|
||||
class NestedScopeObject;
|
||||
|
||||
namespace frontend {
|
||||
struct BytecodeEmitter;
|
||||
class UpvarCookie;
|
||||
class FunctionBox;
|
||||
class ModuleBox;
|
||||
} // namespace frontend
|
||||
|
||||
namespace detail {
|
||||
|
@ -949,6 +951,7 @@ class JSScript : public js::gc::TenuredCell
|
|||
js::HeapPtrObject sourceObject_;
|
||||
|
||||
js::HeapPtrFunction function_;
|
||||
js::HeapPtr<js::ModuleObject*> module_;
|
||||
js::HeapPtrObject enclosingStaticScope_;
|
||||
|
||||
/*
|
||||
|
@ -1139,7 +1142,7 @@ class JSScript : public js::gc::TenuredCell
|
|||
// instead of private to suppress -Wunused-private-field compiler warnings.
|
||||
protected:
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
uint32_t padding;
|
||||
// No padding currently required.
|
||||
#endif
|
||||
|
||||
//
|
||||
|
@ -1167,6 +1170,8 @@ class JSScript : public js::gc::TenuredCell
|
|||
js::frontend::BytecodeEmitter* bce);
|
||||
static void linkToFunctionFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
|
||||
js::frontend::FunctionBox* funbox);
|
||||
static void linkToModuleFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
|
||||
js::frontend::ModuleBox* funbox);
|
||||
// Initialize a no-op script.
|
||||
static bool fullyInitTrivial(js::ExclusiveContext* cx, JS::Handle<JSScript*> script);
|
||||
|
||||
|
@ -1547,6 +1552,11 @@ class JSScript : public js::gc::TenuredCell
|
|||
*/
|
||||
inline void ensureNonLazyCanonicalFunction(JSContext* cx);
|
||||
|
||||
js::ModuleObject* module() const {
|
||||
return module_;
|
||||
}
|
||||
inline void setModule(js::ModuleObject* module);
|
||||
|
||||
JSFlatString* sourceData(JSContext* cx);
|
||||
|
||||
static bool loadSource(JSContext* cx, js::ScriptSource* ss, bool* worked);
|
||||
|
|
|
@ -67,10 +67,18 @@ JSScript::functionDelazifying() const
|
|||
inline void
|
||||
JSScript::setFunction(JSFunction* fun)
|
||||
{
|
||||
MOZ_ASSERT(!function_ && !module_);
|
||||
MOZ_ASSERT(fun->isTenured());
|
||||
function_ = fun;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSScript::setModule(js::ModuleObject* module)
|
||||
{
|
||||
MOZ_ASSERT(!function_ && !module_);
|
||||
module_ = module;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSScript::ensureNonLazyCanonicalFunction(JSContext* cx)
|
||||
{
|
||||
|
|
|
@ -147,6 +147,7 @@ UNIFIED_SOURCES += [
|
|||
'builtin/Eval.cpp',
|
||||
'builtin/Intl.cpp',
|
||||
'builtin/MapObject.cpp',
|
||||
'builtin/ModuleObject.cpp',
|
||||
'builtin/Object.cpp',
|
||||
'builtin/Profilers.cpp',
|
||||
'builtin/Reflect.cpp',
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
#endif
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "builtin/TestingFunctions.h"
|
||||
#include "frontend/Parser.h"
|
||||
#include "gc/GCInternals.h"
|
||||
|
@ -2067,7 +2068,12 @@ DisassembleToSprinter(JSContext* cx, unsigned argc, Value* vp, Sprinter* sprinte
|
|||
} else {
|
||||
for (unsigned i = 0; i < p.argc; i++) {
|
||||
RootedFunction fun(cx);
|
||||
RootedScript script (cx, ValueToScript(cx, p.argv[i], fun.address()));
|
||||
RootedScript script(cx);
|
||||
RootedValue value(cx, p.argv[i]);
|
||||
if (value.isObject() && value.toObject().is<ModuleObject>())
|
||||
script = value.toObject().as<ModuleObject>().script();
|
||||
else
|
||||
script = ValueToScript(cx, value, fun.address());
|
||||
if (!script)
|
||||
return false;
|
||||
if (!DisassembleScript(cx, script, fun, p.lines, p.recursive, sprinter))
|
||||
|
@ -3058,6 +3064,45 @@ Compile(JSContext* cx, unsigned argc, Value* vp)
|
|||
return ok;
|
||||
}
|
||||
|
||||
static bool
|
||||
ParseModule(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() < 1) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_MORE_ARGS_NEEDED, "parseModule", "0", "s");
|
||||
return false;
|
||||
}
|
||||
if (!args[0].isString()) {
|
||||
JS_ReportError(cx, "expected string to compile, got %s",
|
||||
JS_TypeOfValue(cx, args[0]));
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JSFlatString* scriptContents = args[0].toString()->ensureFlat(cx);
|
||||
if (!scriptContents)
|
||||
return false;
|
||||
|
||||
CompileOptions options(cx);
|
||||
options.setFileAndLine("<string>", 1);
|
||||
|
||||
AutoStableStringChars stableChars(cx);
|
||||
if (!stableChars.initTwoByte(cx, scriptContents))
|
||||
return false;
|
||||
|
||||
const char16_t* chars = stableChars.twoByteRange().start().get();
|
||||
SourceBufferHolder srcBuf(chars, scriptContents->length(),
|
||||
SourceBufferHolder::NoOwnership);
|
||||
|
||||
RootedObject module(cx, frontend::CompileModule(cx, global, options, srcBuf));
|
||||
if (!module)
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*module);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
Parse(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
@ -4606,6 +4651,10 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
|||
"compile(code)",
|
||||
" Compiles a string to bytecode, potentially throwing."),
|
||||
|
||||
JS_FN_HELP("parseModule", ParseModule, 1, 0,
|
||||
"parseModule(code)",
|
||||
" Parses source text as a module and returns a Module object."),
|
||||
|
||||
JS_FN_HELP("parse", Parse, 1, 0,
|
||||
"parse(code)",
|
||||
" Parses a string, potentially throwing."),
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace js {
|
|||
namespace frontend {
|
||||
struct Definition;
|
||||
class FunctionBox;
|
||||
class ModuleBox;
|
||||
}
|
||||
|
||||
class StaticWithObject;
|
||||
|
@ -47,6 +48,9 @@ class StaticNonSyntacticScopeObjects;
|
|||
* JSFunction
|
||||
* Scope for function bodies. e.g., |function f() { var x; let y; }|
|
||||
*
|
||||
* ModuleObject
|
||||
* Scope for moddules.
|
||||
*
|
||||
* StaticWithObject
|
||||
* Scope for |with|. e.g., |with ({}) { ... }|
|
||||
*
|
||||
|
@ -63,8 +67,8 @@ class StaticNonSyntacticScopeObjects;
|
|||
*
|
||||
* (function f() { var x; function g() { } })
|
||||
*
|
||||
* All static scope objects are ScopeObjects with the exception of
|
||||
* JSFunction. A JSFunction keeps its enclosing scope link on
|
||||
* All static scope objects are ScopeObjects with the exception of JSFunction
|
||||
* and ModuleObject, which keeps their enclosing scope link on
|
||||
* |JSScript::enclosingStaticScope()|.
|
||||
*/
|
||||
template <AllowGC allowGC>
|
||||
|
@ -78,7 +82,8 @@ class StaticScopeIter
|
|||
obj->is<StaticWithObject>() ||
|
||||
obj->is<StaticEvalObject>() ||
|
||||
obj->is<StaticNonSyntacticScopeObjects>() ||
|
||||
obj->is<JSFunction>();
|
||||
obj->is<JSFunction>() ||
|
||||
obj->is<ModuleObject>();
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
|
@ -672,6 +672,7 @@ TraceLoggerThreadState::init()
|
|||
enabledTextIds[TraceLogger_ParserCompileFunction] = true;
|
||||
enabledTextIds[TraceLogger_ParserCompileLazy] = true;
|
||||
enabledTextIds[TraceLogger_ParserCompileScript] = true;
|
||||
enabledTextIds[TraceLogger_ParserCompileModule] = true;
|
||||
enabledTextIds[TraceLogger_IrregexpCompile] = true;
|
||||
enabledTextIds[TraceLogger_IrregexpExecute] = true;
|
||||
enabledTextIds[TraceLogger_Scripts] = true;
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
_(ParserCompileFunction) \
|
||||
_(ParserCompileLazy) \
|
||||
_(ParserCompileScript) \
|
||||
_(ParserCompileModule) \
|
||||
_(Scripts) \
|
||||
_(VM) \
|
||||
\
|
||||
|
|
Загрузка…
Ссылка в новой задаче