Skip to content

Commit 71af10b

Browse files
authored
Merge pull request RustPython#1744 from RustPython/coolreader18/wasm-inject-module
Add a better injectModule method to WASM
2 parents a104d43 + a8027ab commit 71af10b

File tree

17 files changed

+246
-69
lines changed

17 files changed

+246
-69
lines changed

benchmarks/bench.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,14 @@ fn bench_rustpy_nbody(b: &mut test::Bencher) {
9494

9595
let vm = VirtualMachine::default();
9696

97-
let code = match vm.compile(source, compile::Mode::Single, "<stdin>".to_owned()) {
98-
Ok(code) => code,
99-
Err(e) => panic!("{:?}", e),
100-
};
97+
let code = vm
98+
.compile(source, compile::Mode::Exec, "<stdin>".to_owned())
99+
.unwrap();
101100

102101
b.iter(|| {
103102
let scope = vm.new_scope_with_builtins();
104103
let res: PyResult = vm.run_code_obj(code.clone(), scope);
105-
assert!(res.is_ok());
104+
vm.unwrap_pyresult(res);
106105
})
107106
}
108107

@@ -113,14 +112,13 @@ fn bench_rustpy_mandelbrot(b: &mut test::Bencher) {
113112

114113
let vm = VirtualMachine::default();
115114

116-
let code = match vm.compile(source, compile::Mode::Single, "<stdin>".to_owned()) {
117-
Ok(code) => code,
118-
Err(e) => panic!("{:?}", e),
119-
};
115+
let code = vm
116+
.compile(source, compile::Mode::Exec, "<stdin>".to_owned())
117+
.unwrap();
120118

121119
b.iter(|| {
122120
let scope = vm.new_scope_with_builtins();
123121
let res: PyResult = vm.run_code_obj(code.clone(), scope);
124-
assert!(res.is_ok());
122+
vm.unwrap_pyresult(res);
125123
})
126124
}

bytecode/src/bytecode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub struct CodeObject {
5050

5151
bitflags! {
5252
#[derive(Serialize, Deserialize)]
53-
pub struct CodeFlags: u8 {
53+
pub struct CodeFlags: u16 {
5454
const HAS_DEFAULTS = 0x01;
5555
const HAS_KW_ONLY_DEFAULTS = 0x02;
5656
const HAS_ANNOTATIONS = 0x04;

compiler/src/compile.rs

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,21 @@ struct Compiler<O: OutputStream = BasicOutputStream> {
2929
source_path: Option<String>,
3030
current_source_location: ast::Location,
3131
current_qualified_path: Option<String>,
32+
done_with_future_stmts: bool,
3233
ctx: CompileContext,
33-
optimize: u8,
34+
opts: CompileOpts,
35+
}
36+
37+
#[derive(Debug, Clone)]
38+
pub struct CompileOpts {
39+
/// How optimized the bytecode output should be; any optimize > 0 does
40+
/// not emit assert statements
41+
pub optimize: u8,
42+
}
43+
impl Default for CompileOpts {
44+
fn default() -> Self {
45+
CompileOpts { optimize: 0 }
46+
}
3447
}
3548

3649
#[derive(Clone, Copy)]
@@ -60,31 +73,31 @@ pub fn compile(
6073
source: &str,
6174
mode: Mode,
6275
source_path: String,
63-
optimize: u8,
76+
opts: CompileOpts,
6477
) -> CompileResult<CodeObject> {
6578
match mode {
6679
Mode::Exec => {
6780
let ast = parser::parse_program(source)?;
68-
compile_program(ast, source_path, optimize)
81+
compile_program(ast, source_path, opts)
6982
}
7083
Mode::Eval => {
7184
let statement = parser::parse_statement(source)?;
72-
compile_statement_eval(statement, source_path, optimize)
85+
compile_statement_eval(statement, source_path, opts)
7386
}
7487
Mode::Single => {
7588
let ast = parser::parse_program(source)?;
76-
compile_program_single(ast, source_path, optimize)
89+
compile_program_single(ast, source_path, opts)
7790
}
7891
}
7992
}
8093

8194
/// A helper function for the shared code of the different compile functions
8295
fn with_compiler(
8396
source_path: String,
84-
optimize: u8,
97+
opts: CompileOpts,
8598
f: impl FnOnce(&mut Compiler) -> CompileResult<()>,
8699
) -> CompileResult<CodeObject> {
87-
let mut compiler = Compiler::new(optimize);
100+
let mut compiler = Compiler::new(opts);
88101
compiler.source_path = Some(source_path);
89102
compiler.push_new_code_object("<module>".to_owned());
90103
f(&mut compiler)?;
@@ -97,9 +110,9 @@ fn with_compiler(
97110
pub fn compile_program(
98111
ast: ast::Program,
99112
source_path: String,
100-
optimize: u8,
113+
opts: CompileOpts,
101114
) -> CompileResult<CodeObject> {
102-
with_compiler(source_path, optimize, |compiler| {
115+
with_compiler(source_path, opts, |compiler| {
103116
let symbol_table = make_symbol_table(&ast)?;
104117
compiler.compile_program(&ast, symbol_table)
105118
})
@@ -109,9 +122,9 @@ pub fn compile_program(
109122
pub fn compile_statement_eval(
110123
statement: Vec<ast::Statement>,
111124
source_path: String,
112-
optimize: u8,
125+
opts: CompileOpts,
113126
) -> CompileResult<CodeObject> {
114-
with_compiler(source_path, optimize, |compiler| {
127+
with_compiler(source_path, opts, |compiler| {
115128
let symbol_table = statements_to_symbol_table(&statement)?;
116129
compiler.compile_statement_eval(&statement, symbol_table)
117130
})
@@ -121,9 +134,9 @@ pub fn compile_statement_eval(
121134
pub fn compile_program_single(
122135
ast: ast::Program,
123136
source_path: String,
124-
optimize: u8,
137+
opts: CompileOpts,
125138
) -> CompileResult<CodeObject> {
126-
with_compiler(source_path, optimize, |compiler| {
139+
with_compiler(source_path, opts, |compiler| {
127140
let symbol_table = make_symbol_table(&ast)?;
128141
compiler.compile_program_single(&ast, symbol_table)
129142
})
@@ -134,24 +147,25 @@ where
134147
O: OutputStream,
135148
{
136149
fn default() -> Self {
137-
Compiler::new(0)
150+
Compiler::new(CompileOpts::default())
138151
}
139152
}
140153

141154
impl<O: OutputStream> Compiler<O> {
142-
fn new(optimize: u8) -> Self {
155+
fn new(opts: CompileOpts) -> Self {
143156
Compiler {
144157
output_stack: Vec::new(),
145158
symbol_table_stack: Vec::new(),
146159
nxt_label: 0,
147160
source_path: None,
148161
current_source_location: ast::Location::default(),
149162
current_qualified_path: None,
163+
done_with_future_stmts: false,
150164
ctx: CompileContext {
151165
in_loop: false,
152166
func: FunctionContext::NoFunction,
153167
},
154-
optimize,
168+
opts,
155169
}
156170
}
157171

@@ -314,6 +328,16 @@ impl<O: OutputStream> Compiler<O> {
314328
self.set_source_location(statement.location);
315329
use ast::StatementType::*;
316330

331+
match &statement.node {
332+
// we do this here because `from __future__` still executes that `from` statement at runtime,
333+
// we still need to compile the ImportFrom down below
334+
ImportFrom { module, names, .. } if module.as_deref() == Some("__future__") => {
335+
self.compile_future_features(&names)?
336+
}
337+
// if we find any other statement, stop accepting future statements
338+
_ => self.done_with_future_stmts = true,
339+
}
340+
317341
match &statement.node {
318342
Import { names } => {
319343
// import a, b, c as d
@@ -518,7 +542,7 @@ impl<O: OutputStream> Compiler<O> {
518542
} => self.compile_class_def(name, body, bases, keywords, decorator_list)?,
519543
Assert { test, msg } => {
520544
// if some flag, ignore all assert statements!
521-
if self.optimize == 0 {
545+
if self.opts.optimize == 0 {
522546
let end_label = self.new_label();
523547
self.compile_jump_if(test, true, end_label)?;
524548
self.emit(Instruction::LoadName {
@@ -2112,6 +2136,28 @@ impl<O: OutputStream> Compiler<O> {
21122136
Ok(())
21132137
}
21142138

2139+
fn compile_future_features(
2140+
&mut self,
2141+
features: &[ast::ImportSymbol],
2142+
) -> Result<(), CompileError> {
2143+
if self.done_with_future_stmts {
2144+
return Err(self.error(CompileErrorType::InvalidFuturePlacement));
2145+
}
2146+
for feature in features {
2147+
match &*feature.symbol {
2148+
// Python 3 features; we've already implemented them by default
2149+
"nested_scopes" | "generators" | "division" | "absolute_import"
2150+
| "with_statement" | "print_function" | "unicode_literals" => {}
2151+
// "generator_stop" => {}
2152+
// "annotations" => {}
2153+
other => {
2154+
return Err(self.error(CompileErrorType::InvalidFutureFeature(other.to_owned())))
2155+
}
2156+
}
2157+
}
2158+
Ok(())
2159+
}
2160+
21152161
// Scope helpers:
21162162
fn enter_scope(&mut self) {
21172163
// println!("Enter scope {:?}", self.symbol_table_stack);

compiler/src/error.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ pub enum CompileErrorType {
5858
InvalidAwait,
5959
AsyncYieldFrom,
6060
AsyncReturnValue,
61+
InvalidFuturePlacement,
62+
InvalidFutureFeature(String),
6163
}
6264

6365
impl CompileError {
@@ -106,6 +108,12 @@ impl fmt::Display for CompileError {
106108
CompileErrorType::AsyncReturnValue => {
107109
"'return' with value inside async generator".to_owned()
108110
}
111+
CompileErrorType::InvalidFuturePlacement => {
112+
"from __future__ imports must occur at the beginning of the file".to_owned()
113+
}
114+
CompileErrorType::InvalidFutureFeature(feat) => {
115+
format!("future feature {} is not defined", feat)
116+
}
109117
};
110118

111119
if let Some(statement) = &self.statement {

derive/src/compile_bytecode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ impl CompilationSource {
4949
module_name: String,
5050
origin: F,
5151
) -> Result<CodeObject, Diagnostic> {
52-
compile::compile(source, mode, module_name, 0).map_err(|err| {
52+
compile::compile(source, mode, module_name, Default::default()).map_err(|err| {
5353
Diagnostic::spans_error(
5454
self.span,
5555
format!("Python compile error from {}: {}", origin(), err),

examples/dis.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,14 @@ fn main() {
6262
let optimize = matches.occurrences_of("optimize") as u8;
6363
let scripts = matches.values_of_os("scripts").unwrap();
6464

65+
let opts = compile::CompileOpts {
66+
optimize,
67+
..Default::default()
68+
};
69+
6570
for script in scripts.map(Path::new) {
6671
if script.exists() && script.is_file() {
67-
let res = display_script(script, mode, optimize, expand_codeobjects);
72+
let res = display_script(script, mode, opts.clone(), expand_codeobjects);
6873
if let Err(e) = res {
6974
error!("Error while compiling {:?}: {}", script, e);
7075
}
@@ -77,11 +82,11 @@ fn main() {
7782
fn display_script(
7883
path: &Path,
7984
mode: compile::Mode,
80-
optimize: u8,
85+
opts: compile::CompileOpts,
8186
expand_codeobjects: bool,
8287
) -> Result<(), Box<dyn Error>> {
8388
let source = fs::read_to_string(path)?;
84-
let code = compile::compile(&source, mode, path.to_string_lossy().into_owned(), optimize)?;
89+
let code = compile::compile(&source, mode, path.to_string_lossy().into_owned(), opts)?;
8590
println!("{}:", path.display());
8691
if expand_codeobjects {
8792
println!("{}", code.display_expand_codeobjects());

tests/snippets/function.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def foo():
1313
assert foo.__name__ == "foo"
1414
assert foo.__qualname__ == "foo"
1515
assert foo.__module__ == "function"
16+
assert foo.__globals__ is globals()
1617

1718
def my_func(a,):
1819
return a+2

tests/snippets/invalid_syntax.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,9 @@ def valid_func():
77
yield 2
88
"""
99

10-
try:
10+
with assert_raises(SyntaxError) as ae:
1111
compile(src, 'test.py', 'exec')
12-
except SyntaxError as ex:
13-
assert ex.lineno == 5
14-
else:
15-
raise AssertionError("Must throw syntax error")
12+
assert ae.exception.lineno == 5
1613

1714
src = """
1815
if True:
@@ -60,3 +57,23 @@ def valid_func():
6057

6158
with assert_raises(SyntaxError):
6259
compile(src, 'test.py', 'exec')
60+
61+
src = """
62+
from __future__ import not_a_real_future_feature
63+
"""
64+
65+
with assert_raises(SyntaxError):
66+
compile(src, 'test.py', 'exec')
67+
68+
src = """
69+
a = 1
70+
from __future__ import print_function
71+
"""
72+
73+
with assert_raises(SyntaxError):
74+
compile(src, 'test.py', 'exec')
75+
76+
src = """
77+
from __future__ import print_function
78+
"""
79+
compile(src, 'test.py', 'exec')

vm/src/import.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,8 @@ pub fn import_file(
7474
file_path: String,
7575
content: String,
7676
) -> PyResult {
77-
let code_obj = compile::compile(
78-
&content,
79-
compile::Mode::Exec,
80-
file_path,
81-
vm.settings.optimize,
82-
)
83-
.map_err(|err| vm.new_syntax_error(&err))?;
77+
let code_obj = compile::compile(&content, compile::Mode::Exec, file_path, vm.compile_opts())
78+
.map_err(|err| vm.new_syntax_error(&err))?;
8479
import_codeobj(vm, module_name, code_obj, true)
8580
}
8681

vm/src/obj/objcode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl PyCodeRef {
103103
}
104104

105105
#[pyproperty]
106-
fn co_flags(self) -> u8 {
106+
fn co_flags(self) -> u16 {
107107
self.code.flags.bits()
108108
}
109109
}

vm/src/obj/objfunction.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,11 @@ impl PyFunction {
278278
fn kwdefaults(&self) -> Option<PyDictRef> {
279279
self.kw_only_defaults.clone()
280280
}
281+
282+
#[pyproperty(magic)]
283+
fn globals(&self) -> PyDictRef {
284+
self.scope.globals.clone()
285+
}
281286
}
282287

283288
#[pyclass]

0 commit comments

Comments
 (0)