1 | /* |
2 | * Copyright (C) 1999-2001 Harri Porten ([email protected]) |
3 | * Copyright (C) 2001 Peter Kelly ([email protected]) |
4 | * Copyright (C) 2003-2019 Apple Inc. |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Library General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Library General Public License |
17 | * along with this library; see the file COPYING.LIB. If not, write to |
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | * |
21 | */ |
22 | |
23 | #include "config.h" |
24 | #include "Completion.h" |
25 | |
26 | #include "BytecodeCacheError.h" |
27 | #include "CallFrame.h" |
28 | #include "CatchScope.h" |
29 | #include "CodeCache.h" |
30 | #include "CodeProfiling.h" |
31 | #include "Exception.h" |
32 | #include "IdentifierInlines.h" |
33 | #include "Interpreter.h" |
34 | #include "JSCInlines.h" |
35 | #include "JSGlobalObject.h" |
36 | #include "JSInternalPromise.h" |
37 | #include "JSLock.h" |
38 | #include "JSModuleLoader.h" |
39 | #include "JSModuleRecord.h" |
40 | #include "JSWithScope.h" |
41 | #include "ModuleAnalyzer.h" |
42 | #include "Parser.h" |
43 | #include "ProgramExecutable.h" |
44 | #include "ScriptProfilingScope.h" |
45 | |
46 | namespace JSC { |
47 | |
48 | static inline bool checkSyntaxInternal(VM& vm, const SourceCode& source, ParserError& error) |
49 | { |
50 | return !!parse<ProgramNode>( |
51 | vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, |
52 | JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded, error); |
53 | } |
54 | |
55 | bool checkSyntax(JSGlobalObject* globalObject, const SourceCode& source, JSValue* returnedException) |
56 | { |
57 | VM& vm = globalObject->vm(); |
58 | JSLockHolder lock(vm); |
59 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
60 | |
61 | ParserError error; |
62 | if (checkSyntaxInternal(vm, source, error)) |
63 | return true; |
64 | ASSERT(error.isValid()); |
65 | if (returnedException) |
66 | *returnedException = error.toErrorObject(globalObject, source); |
67 | return false; |
68 | } |
69 | |
70 | bool checkSyntax(VM& vm, const SourceCode& source, ParserError& error) |
71 | { |
72 | JSLockHolder lock(vm); |
73 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
74 | return checkSyntaxInternal(vm, source, error); |
75 | } |
76 | |
77 | bool checkModuleSyntax(JSGlobalObject* globalObject, const SourceCode& source, ParserError& error) |
78 | { |
79 | VM& vm = globalObject->vm(); |
80 | JSLockHolder lock(vm); |
81 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
82 | std::unique_ptr<ModuleProgramNode> moduleProgramNode = parse<ModuleProgramNode>( |
83 | vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, |
84 | JSParserStrictMode::Strict, JSParserScriptMode::Module, SourceParseMode::ModuleAnalyzeMode, SuperBinding::NotNeeded, error); |
85 | if (!moduleProgramNode) |
86 | return false; |
87 | |
88 | PrivateName privateName(PrivateName::Description, "EntryPointModule" ); |
89 | ModuleAnalyzer moduleAnalyzer(globalObject, Identifier::fromUid(privateName), source, moduleProgramNode->varDeclarations(), moduleProgramNode->lexicalVariables()); |
90 | moduleAnalyzer.analyze(*moduleProgramNode); |
91 | return true; |
92 | } |
93 | |
94 | RefPtr<CachedBytecode> generateProgramBytecode(VM& vm, const SourceCode& source, FileSystem::PlatformFileHandle fd, BytecodeCacheError& error) |
95 | { |
96 | JSLockHolder lock(vm); |
97 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
98 | |
99 | VariableEnvironment variablesUnderTDZ; |
100 | JSParserStrictMode strictMode = JSParserStrictMode::NotStrict; |
101 | JSParserScriptMode scriptMode = JSParserScriptMode::Classic; |
102 | EvalContextType evalContextType = EvalContextType::None; |
103 | |
104 | ParserError parserError; |
105 | UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedProgramCodeBlock>(vm, source, strictMode, scriptMode, { }, parserError, evalContextType, &variablesUnderTDZ); |
106 | if (parserError.isValid()) |
107 | error = parserError; |
108 | if (!unlinkedCodeBlock) |
109 | return nullptr; |
110 | |
111 | return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ProgramType, strictMode, scriptMode, fd, error, { }); |
112 | } |
113 | |
114 | RefPtr<CachedBytecode> generateModuleBytecode(VM& vm, const SourceCode& source, FileSystem::PlatformFileHandle fd, BytecodeCacheError& error) |
115 | { |
116 | JSLockHolder lock(vm); |
117 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
118 | |
119 | VariableEnvironment variablesUnderTDZ; |
120 | JSParserStrictMode strictMode = JSParserStrictMode::Strict; |
121 | JSParserScriptMode scriptMode = JSParserScriptMode::Module; |
122 | EvalContextType evalContextType = EvalContextType::None; |
123 | |
124 | ParserError parserError; |
125 | UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, source, strictMode, scriptMode, { }, parserError, evalContextType, &variablesUnderTDZ); |
126 | if (parserError.isValid()) |
127 | error = parserError; |
128 | if (!unlinkedCodeBlock) |
129 | return nullptr; |
130 | return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ModuleType, strictMode, scriptMode, fd, error, { }); |
131 | } |
132 | |
133 | JSValue evaluate(JSGlobalObject* globalObject, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException) |
134 | { |
135 | VM& vm = globalObject->vm(); |
136 | JSLockHolder lock(vm); |
137 | auto scope = DECLARE_CATCH_SCOPE(vm); |
138 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
139 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
140 | |
141 | CodeProfiling profile(source); |
142 | |
143 | if (!thisValue || thisValue.isUndefinedOrNull()) |
144 | thisValue = globalObject; |
145 | JSObject* thisObj = jsCast<JSObject*>(thisValue.toThis(globalObject, NotStrictMode)); |
146 | JSValue result = vm.interpreter->executeProgram(source, globalObject, thisObj); |
147 | |
148 | if (scope.exception()) { |
149 | returnedException = scope.exception(); |
150 | scope.clearException(); |
151 | return jsUndefined(); |
152 | } |
153 | |
154 | RELEASE_ASSERT(result); |
155 | return result; |
156 | } |
157 | |
158 | JSValue profiledEvaluate(JSGlobalObject* globalObject, ProfilingReason reason, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException) |
159 | { |
160 | ScriptProfilingScope profilingScope(globalObject, reason); |
161 | return evaluate(globalObject, source, thisValue, returnedException); |
162 | } |
163 | |
164 | JSValue evaluateWithScopeExtension(JSGlobalObject* globalObject, const SourceCode& source, JSObject* scopeExtensionObject, NakedPtr<Exception>& returnedException) |
165 | { |
166 | VM& vm = globalObject->vm(); |
167 | |
168 | if (scopeExtensionObject) { |
169 | JSScope* ignoredPreviousScope = globalObject->globalScope(); |
170 | globalObject->setGlobalScopeExtension(JSWithScope::create(vm, globalObject, ignoredPreviousScope, scopeExtensionObject)); |
171 | } |
172 | |
173 | JSValue returnValue = JSC::evaluate(globalObject, source, globalObject, returnedException); |
174 | |
175 | if (scopeExtensionObject) |
176 | globalObject->clearGlobalScopeExtension(); |
177 | |
178 | return returnValue; |
179 | } |
180 | |
181 | static Symbol* createSymbolForEntryPointModule(VM& vm) |
182 | { |
183 | // Generate the unique key for the source-provided module. |
184 | PrivateName privateName(PrivateName::Description, "EntryPointModule" ); |
185 | return Symbol::create(vm, privateName.uid()); |
186 | } |
187 | |
188 | static JSInternalPromise* rejectPromise(JSGlobalObject* globalObject) |
189 | { |
190 | VM& vm = globalObject->vm(); |
191 | auto scope = DECLARE_CATCH_SCOPE(vm); |
192 | scope.assertNoException(); |
193 | JSValue exception = scope.exception()->value(); |
194 | scope.clearException(); |
195 | JSInternalPromise* promise = JSInternalPromise::create(vm, globalObject->internalPromiseStructure()); |
196 | promise->reject(globalObject, exception); |
197 | return promise; |
198 | } |
199 | |
200 | JSInternalPromise* loadAndEvaluateModule(JSGlobalObject* globalObject, Symbol* moduleId, JSValue parameters, JSValue scriptFetcher) |
201 | { |
202 | VM& vm = globalObject->vm(); |
203 | JSLockHolder lock(vm); |
204 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
205 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
206 | |
207 | return globalObject->moduleLoader()->loadAndEvaluateModule(globalObject, moduleId, parameters, scriptFetcher); |
208 | } |
209 | |
210 | JSInternalPromise* loadAndEvaluateModule(JSGlobalObject* globalObject, const String& moduleName, JSValue parameters, JSValue scriptFetcher) |
211 | { |
212 | VM& vm = globalObject->vm(); |
213 | JSLockHolder lock(vm); |
214 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
215 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
216 | |
217 | return globalObject->moduleLoader()->loadAndEvaluateModule(globalObject, identifierToJSValue(vm, Identifier::fromString(vm, moduleName)), parameters, scriptFetcher); |
218 | } |
219 | |
220 | JSInternalPromise* loadAndEvaluateModule(JSGlobalObject* globalObject, const SourceCode& source, JSValue scriptFetcher) |
221 | { |
222 | VM& vm = globalObject->vm(); |
223 | JSLockHolder lock(vm); |
224 | auto scope = DECLARE_THROW_SCOPE(vm); |
225 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
226 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
227 | |
228 | Symbol* key = createSymbolForEntryPointModule(vm); |
229 | |
230 | // Insert the given source code to the ModuleLoader registry as the fetched registry entry. |
231 | globalObject->moduleLoader()->provideFetch(globalObject, key, source); |
232 | RETURN_IF_EXCEPTION(scope, rejectPromise(globalObject)); |
233 | |
234 | return globalObject->moduleLoader()->loadAndEvaluateModule(globalObject, key, jsUndefined(), scriptFetcher); |
235 | } |
236 | |
237 | JSInternalPromise* loadModule(JSGlobalObject* globalObject, const String& moduleName, JSValue parameters, JSValue scriptFetcher) |
238 | { |
239 | VM& vm = globalObject->vm(); |
240 | JSLockHolder lock(vm); |
241 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
242 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
243 | |
244 | return globalObject->moduleLoader()->loadModule(globalObject, identifierToJSValue(vm, Identifier::fromString(vm, moduleName)), parameters, scriptFetcher); |
245 | } |
246 | |
247 | JSInternalPromise* loadModule(JSGlobalObject* globalObject, const SourceCode& source, JSValue scriptFetcher) |
248 | { |
249 | VM& vm = globalObject->vm(); |
250 | JSLockHolder lock(vm); |
251 | auto scope = DECLARE_THROW_SCOPE(vm); |
252 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
253 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
254 | |
255 | Symbol* key = createSymbolForEntryPointModule(vm); |
256 | |
257 | // Insert the given source code to the ModuleLoader registry as the fetched registry entry. |
258 | // FIXME: Introduce JSSourceCode object to wrap around this source. |
259 | globalObject->moduleLoader()->provideFetch(globalObject, key, source); |
260 | RETURN_IF_EXCEPTION(scope, rejectPromise(globalObject)); |
261 | |
262 | return globalObject->moduleLoader()->loadModule(globalObject, key, jsUndefined(), scriptFetcher); |
263 | } |
264 | |
265 | JSValue linkAndEvaluateModule(JSGlobalObject* globalObject, const Identifier& moduleKey, JSValue scriptFetcher) |
266 | { |
267 | VM& vm = globalObject->vm(); |
268 | JSLockHolder lock(vm); |
269 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
270 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
271 | |
272 | return globalObject->moduleLoader()->linkAndEvaluateModule(globalObject, identifierToJSValue(vm, moduleKey), scriptFetcher); |
273 | } |
274 | |
275 | JSInternalPromise* importModule(JSGlobalObject* globalObject, const Identifier& moduleKey, JSValue parameters, JSValue scriptFetcher) |
276 | { |
277 | VM& vm = globalObject->vm(); |
278 | JSLockHolder lock(vm); |
279 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
280 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
281 | |
282 | return globalObject->moduleLoader()->requestImportModule(globalObject, moduleKey, parameters, scriptFetcher); |
283 | } |
284 | |
285 | } // namespace JSC |
286 | |